如果我将一个对象传递给一个方法,为什么我应该使用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”,这意味着对象作为引用传递。
如果你想改变对象是什么,传递一个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并不指向原来的新TestRef,而是指向一个完全不同的对象。
如果你想改变一个不可变对象的值,比如一个字符串,这可能也很有用。一旦创建了字符串,就不能更改它的值。但是通过使用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;
}
在. 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; }
}
}
将引用类型(例如List<T>)的变量(例如foo)视为持有形式为“object# 24601”的对象标识符。假设语句foo = new List<int> {1,5,7,9};使foo保存“Object #24601”(一个包含四个项的列表)。然后调用foo。Length将向object# 24601请求其长度,它将响应4,因此输入foo。长度等于4。
如果foo被传递给一个方法而没有使用ref,该方法可能会对Object #24601进行更改。作为这些变化的结果,foo。Length可能不再等于4。然而,方法本身将无法更改foo,它将继续保存“object# 24601”。
将foo作为ref参数传递将允许被调用的方法不仅对Object #24601进行更改,而且对foo本身进行更改。该方法可以创建一个新的对象#8675309,并在foo中存储对该对象的引用。如果这样做,foo将不再保存“object# 24601”,而是保存“object# 8675309”。
In practice, reference-type variables don't hold strings of the form "Object #8675309"; they don't even hold anything that can be meaningfully converted into a number. Even though each reference-type variable will hold some bit pattern, there is no fixed relationship between the bit patterns stored in such variables and the objects they identify. There is no way code could extract information from an object or a reference to it, and later determine whether another reference identified the same object, unless the code either held or knew of a reference that identified the original object.