如果我将对象传递给方法,为什么要使用ref关键字?这不是默认行为吗?
例如:
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”,表示该对象作为参考传递 .
11 回答
如果要更改对象的内容,请传递
ref
:在调用DoSomething之后,
t
不会引用原始的new TestRef
,而是指一个完全不同的对象 .如果您想要更改不可变对象的值,例如,这可能也很有用 . a
string
. 创建后,您无法更改string
的值 . 但是通过使用ref
,您可以创建一个函数来更改另一个具有不同值的字符串 .编辑:正如其他人提到的那样 . 除非需要,否则使用
ref
不是一个好主意 . 使用ref
赋予方法自由更改其他内容的参数,需要对方法的调用者进行编码以确保它们处理这种可能性 .此外,当参数类型是对象时,对象变量始终充当对象的引用 . 这意味着当使用
ref
关键字时,您已获得对引用的引用 . 这允许您执行上面给出的示例中描述的操作 . 但是,当参数类型是原始值(例如int
)时,如果在方法中指定了此参数,则在方法返回后将传入的参数值将更改:您需要区分“按值传递引用”和“通过引用传递参数/参数” .
我写了一个reasonably long article on the subject,以避免每次出现在新闻组时都要小心写:)
在.NET中,当您将任何参数传递给方法时,都会创建一个副本 . 在值类型中,表示对值进行的任何修改都在方法范围内,并在退出方法时丢失 .
传递引用类型时,也会复制一个副本,但它是引用的副本,即现在内存中有两个引用同一对象 . 因此,如果使用引用来修改对象,则会对其进行修改 . 但是如果您修改引用本身 - 我们必须记住它是一个副本 - 那么在退出该方法时也会丢失任何更改 .
正如人们之前所说的那样,赋值是对引用的修改,因此丢失了:
上述方法不会修改原始对象 .
对你的例子进行一点修改
由于TestRef是一个类(它们是引用对象),因此您可以更改t中的内容而不将其作为引用传递 . 但是,如果将t作为ref传递,则TestRef可以更改原始t所指的内容 . 即使它指向不同的对象 .
使用
ref
,你可以写:方法完成后,t将被更改 .
将引用类型的变量(例如
foo
)(例如List<T>
)视为保持形式"Object #24601"的对象标识符 . 假设语句foo = new List<int> {1,5,7,9};
导致foo
持有"Object #24601"(包含四个项目的列表) . 然后调用foo.Length
将询问对象#24601的长度,它将响应4,因此foo.Length
将等于4 .如果在不使用
ref
的情况下将foo
传递给方法,则该方法可能会对对象#24601进行更改 . 由于此类更改,foo.Length
可能不再等于4.但是,方法本身将无法更改foo
,这将继续保持"Object #24601" .将
foo
作为ref
参数传递将允许被调用的方法不仅对对象#24601进行更改,而且对foo
本身进行更改 . 该方法可能会创建一个新的对象#8675309并在foo
中存储对该对象的引用 . 如果它这样做,foo
将不再持有"Object #24601",而是"Object #8675309" .实际上,引用类型变量不包含“Object#8675309”形式的字符串;他们甚至没有任何可以有意义地转换成数字的东西 . 尽管每个引用类型变量将保持一些位模式,但存储在这些变量中的位模式与它们识别的对象之间没有固定的关系 . 代码无法从对象或对它的引用中提取信息,并且稍后确定另一个引用是否标识了相同的对象,除非代码持有或知道标识原始的引用宾语 .
这就像将指针传递给C中的指针一样 . 在.NET中,这将允许您更改原始T所指的内容,但我认为如果您在.NET中这样做,则可能存在设计问题!
通过将
ref
关键字与引用类型一起使用,您实际上是将引用传递给引用 . 在许多方面,它与使用out
关键字相同,但有一点不同,即无法保证该方法实际上会为ref
'ed参数指定任何内容 .ref
模仿(或行为)仅作为两个范围的全局区域:来电者
被叫 .
但是,如果你传递了一个值,那么事情会有所不同 . 您可以强制通过引用传递值 . 例如,这允许您将整数传递给方法,并让该方法代表您修改整数 .
Ref表示函数是否可以触及对象本身,或仅表示其值 .
通过引用传递不受语言约束;它是一个参数绑定策略,旁边是按值传递,按名称传递,按需传递等...
旁注:在此上下文中,类名
TestRef
是一个非常糟糕的选择;) .