C# 为什么要使用';参考';传递对象时的关键字?
如果将对象传递给方法,为什么要使用ref关键字?这不是默认的行为吗 例如:C# 为什么要使用';参考';传递对象时的关键字?,c#,.net,pass-by-reference,ref,C#,.net,Pass By Reference,Ref,如果将对象传递给方法,为什么要使用ref关键字?这不是默认的行为吗 例如: class Program { static void Main(string[] args) { TestRef t = new TestRef(); t.Something = "Foo"; DoSomething(t); Console.WriteLine(t.Something); } static public
class Program
{
static void Main(string[] args)
{
TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(t);
Console.WriteLine(t.Something);
}
static public void DoSomething(TestRef t)
{
t.Something = "Bar";
}
}
public class TestRef
{
public string Something { get; set; }
}
输出为“Bar”,这意味着对象作为引用传递。但是,如果传递值,情况就不同了。您可以强制通过引用传递值。例如,这允许您将一个整数传递给一个方法,并让该方法代表您修改该整数。使用
ref
可以编写:
static public void DoSomething(ref TestRef t)
{
t = new TestRef();
}
方法完成后,t将被更改。如果要更改对象的内容,请传递a
ref
:
TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(ref t);
void DoSomething(ref TestRef t)
{
t = new TestRef();
t.Something = "Not just a changed t, but a completely different TestRef object";
}
调用DoSomething后,t
不引用原始的newtestref
,而是引用一个完全不同的对象
如果要更改不可变对象(例如字符串
)的值,这可能也很有用。一旦创建了字符串,就不能更改其值。但是通过使用ref
,可以创建一个函数,将字符串更改为另一个具有不同值的字符串
除非需要,否则使用ref
不是一个好主意。使用ref
可以让方法自由地更改其他对象的参数,需要对方法的调用方进行编码,以确保他们能够处理这种可能性
此外,当参数类型为对象时,对象变量始终充当对象的引用。这意味着当使用ref
关键字时,您已经获得了对引用的引用。这允许您按照上面给出的示例中所述进行操作。但是,当参数类型为基元值(例如,int
)时,如果在方法中指定了此参数,则在方法返回后,传入的参数值将发生更改:
int x = 1;
Change(ref x);
Debug.Assert(x == 5);
WillNotChange(x);
Debug.Assert(x == 5); // Note: x doesn't become 10
void Change(ref int x)
{
x = 5;
}
void WillNotChange(int x)
{
x = 10;
}
由于TestRef是一个类(它们是引用对象),您可以更改t中的内容,而无需将其作为引用传递。但是,如果您将t作为引用传递,TestRef可以更改原始t引用的内容。i、 e.使其指向不同的对象。通过将ref
关键字与引用类型一起使用,可以有效地将引用传递给引用。在许多方面,它与使用out
关键字相同,但有一个细微的区别,即不能保证该方法将实际为ref
'ed参数分配任何内容 这就像在.NET中向C中的指针传递指针。这将允许您更改原始T所指的内容,但我个人认为,如果您在.NET中这样做,您可能会遇到设计问题 您需要区分“通过值传递引用”和“通过引用传递参数/参数”
为了避免每次在.NET中的新闻组上出现这种情况时都必须仔细编写,我编写了一个方法,当您将任何参数传递给一个方法时,就会创建一个副本。在值类型中,意味着您对值所做的任何修改都在方法范围内,并且在退出方法时丢失
传递引用类型时,也会创建一个副本,但它是引用的副本,即,现在内存中有两个对同一对象的引用。因此,如果使用引用来修改对象,它将被修改。但是如果您修改了引用本身——我们必须记住它是一个副本——那么在退出该方法时,任何更改都将丢失
正如人们之前所说,赋值是对引用的修改,因此会丢失:
public void Method1(object obj) {
obj = new Object();
}
public void Method2(object obj) {
obj = _privateObject;
}
上述方法不会修改原始对象
对你的例子稍加修改
using System;
class Program
{
static void Main(string[] args)
{
TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(t);
Console.WriteLine(t.Something);
}
static public void DoSomething(TestRef t)
{
t = new TestRef();
t.Something = "Bar";
}
}
public class TestRef
{
private string s;
public string Something
{
get {return s;}
set { s = value; }
}
}
ref
仅在两个范围内模拟(或表现)为全局区域:
- 来电者
- 卡利
将引用类型(例如列表
)的变量(例如foo
)视为持有形式为“object#24601”的对象标识符。假设语句foo=newlist{1,5,7,9}代码>使foo
保存“Object#24601”(包含四项的列表)。然后调用foo.Length
将询问对象#24601的长度,它将响应4,因此foo.Length
将等于4
如果将foo
传递给方法而不使用ref
,则该方法可能会更改对象#24601。由于这些更改,foo.Length
可能不再等于4。但是,该方法本身将无法更改foo
,它将继续保持“Object#24601”
将foo
作为ref
参数传递将允许被调用的方法不仅对对象#24601进行更改,而且对foo
本身进行更改。该方法可能会创建一个新对象#8675309,并在foo
中存储对该对象的引用。如果这样做,foo
将不再持有“Object#24601”,而是持有“Object#8675309”
实际上,引用类型变量不包含“Object#8675309”形式的字符串;它们甚至没有任何可以有意义地转换成数字的东西。即使每个引用类型变量都会包含一些位模式,但存储在这些变量中的位模式与它们标识的对象之间没有固定的关系。代码无法从一个对象或对该对象的引用中提取信息,然后确定另一个引用是否标识了同一个对象,除非代码持有或知道标识了原始对象的引用。无论是传递引用还是值类型值,默认行为是按值传递。您只需要了解,对于引用类型,您传递的值是一个引用。这与通过引用传递不同。我在将VB6升级到.NETC#代码时遇到了这个问题。有一些函数/方法签名可以获取ref、out和plain参数。那么,我们怎样才能更好地区分不同的plai呢