为什么在c#中引用类型不更新
本文关键字:更新 引用类型 为什么 | 更新日期: 2023-09-27 18:02:32
这是我的c#代码。为什么在c#中引用类型不更新,下面是代码
class Program
{
public static void Main(string[] args)
{
var a = A.field;
A.field = "2";
Console.WriteLine(a);
Console.Read();
}
}
public static class A
{
public static string field = "1";
}
结果是1,为什么??
public static void Main(string[] args)
{
var a = A.field; // `a` is now a reference to the string "1"
A.field = "2"; // `A.field` is now a reference to the string "2"
Console.WriteLine(a); // nothing else has changed, so `a` is still a reference to the string "1"
Console.Read();
}
所以你的问题的答案基本上是:引用不更新,因为你没有改变你写到控制台(a
)的引用,而是另一个引用(A.field
)。
您所看到的行为并不局限于引用类型。对于值类型也会发生同样的事情:
int x = 1;
int y = x;
x = 2; // y is still 1 at this point
这是因为y
变量存储了1
的值,而改变x
的值并不会自动改变y
的值。x
不"知道"y
。
这与引用类型相同-在您的示例中,a
在赋值后指向与A.field
相同的内存位置,但它不"知道"A.field
指向何处。因此改变A.field
的引用不影响a
。
一个类比:
你给Joe一张卡片,上面写着Cat。设Ann = Joe
表示安将从你那里收到一张卡片,上面写着和乔卡片上写的一样的东西。所以现在他们的卡片上都写着Cat。现在,如果你给Joe另一张卡片,上面写着狗, Ann的卡片上仍然写着猫。这个类比适用于值类型,对于引用类型,您将在卡片上写一个位置,指向可以找到单词的位置。同样的原则仍然适用。
这与以下相同:
String a = "1";
String b = a;
b = "2";
Console.Write($"a:{a} b:{b}");
最初,您有一个变量a
,它引用内容为1
的对象,以及一个变量b
,它引用相同的对象。然后修改变量b
以引用内容为2
的新对象。如果不修改由a
或b
引用的对象,则使变量引用不同的对象。您必须修改b
引用的对象,以便a
变量"看到"更改。唉,由于String
是不可变的,因此根本没有办法实际修改它。您可以使用的任何API都将导致b
引用的新字符串,并且a
看起来与以前相同。对于可变类型,只要修改对象,而不是简单地将新对象赋值给变量,就可以看到区别:
var a = new Widget {x = 1};
var b = a;
b.x = 2;
Console.Write($"a.x: {a.x} b.x: {b.x}");
您正在分配引用而未修改原始引用。
当您将A.field
赋值给a
时,您将字符串"1"
的引用赋值给a
。之后你给A.field
赋了一个新的字符串,a
的原始值没有变化。它仍然保留对字符串"1"
的旧引用。
如果以某种方式,您可以修改原始引用,那么您应该能够看到更改。由于您的类型是string,因此您不能真正修改它,因为它是不可变的,但是考虑一个StringBuilder
的示例。
public static class A
{
public static StringBuilder field { get; set; } = new StringBuilder("1");
}
。
static void Main(string[] args)
{
var a = A.field;
A.field.Insert(0, "2");
Console.WriteLine(a);
Console.Read();
}
现在您将在变量a
中获得修改后的值"21"
。
注意,上面的代码用A.field.Insert(0, "2");
修改了字段,因为变量a
保存了对A.field
的引用,所以在下一行可以看到更改。
但是如果你试图用这样的语句给A.field
分配一个新的引用:
A.field = new StringBuilder("2");
那么A.field
将有一个新的对象来引用,而之前的变量a
将仍然保持旧的引用。
你可以把"reference"类型变量想象成一个方框,它是某个资源的地址。
让我们考虑一个string
值。当我们将文字值string
赋值给"引用"时,我们不会做以下事情:
- 分配一个内存区域(静态或堆)设置指定的值;
- 分配另一个内存区域(静态或堆)用字符串值的内存地址(引用)初始化;
当我们将一个字符串"引用"类型var赋值给另一个"引用"时,我们没有做任何事情,只是将被引用的string
值的内存地址赋值给新的"引用"。
因此,如果我们用一个新的赋值语句改变了第一个"引用",第二个"引用"将不受影响,它将继续引用原始值。
最后,在c#中不可能(至少现在)有"引用到引用",你可以在c++中拥有它(指针和引用是有点不同的东西…)。参见Eric Lippert对类似问题的回答:https://stackoverflow.com/a/15328414/3762855
变量a
获取静态属性A.field
的值的副本。
所以当你修改A.field
时,副本不会改变
try this
class Program
{
static void Main(string[] args)
{
A.field = "2";
var a = A.field;
Console.WriteLine(a);
Console.Read();
}
}
public static class A
{
public static string field = "1";
}