引用类型在 .NET 中通过“按值引用”传递。这意味着为实际参数分配不同的值实际上并不会改变原始值(除非您使用 ByRef/ref)。但是,您对传入的实际对象所做的任何更改都会更改调用方法所引用的对象。例如,考虑以下程序:
void Main()
{
var a = new A{I=1};
Console.WriteLine(a.I);
DoSomething(a);
Console.WriteLine(a.I);
DoSomethingElse(a);
Console.WriteLine(a.I);
}
public void DoSomething(A a)
{
a = new A{I=2};
}
public void DoSomethingElse(A a)
{
a.I = 2;
}
public class A
{
public int I;
}
输出:
1
1
2
该DoSomething
方法为其a
参数分配了不同的值,但该参数只是一个本地指针,指向a
调用方法的原始位置。更改指针的值不会更改调用方法的a
值。但是,DoSomethingElse
实际上对引用对象上的值之一进行了更改。
不管其他回答者怎么说,string
这种方式也不例外。所有对象都以这种方式运行。
与string
许多对象的不同之处在于它是不可变的:字符串上没有任何方法、属性或字段可以调用以实际更改字符串。在 .NET 中创建字符串后,它就是只读的。
当你做这样的事情时:
var s = "hello";
s += " world";
...编译器把它变成这样的东西:
// this is compiled into the assembly, and doesn't need to be set at runtime.
const string S1 = "hello";
const string S2 = " world"; // likewise
string s = S1;
s = new StringBuilder().Append(s).Append(S2).ToString();
最后一行生成了一个新字符串,但 S1 和 S2 仍然存在。如果它们是内置于程序集中的常量字符串,它们将留在那里。如果它们是动态创建的并且没有更多对它们的引用,则垃圾收集器可以取消引用它们以释放内存。但关键是要意识到 S1 从未真正改变过。指向它的变量只是更改为指向不同的字符串。