这是一个简单的代码片段,但让我有些困惑:
string s1 = "abc";
string s2 = s1;
s2 = "123";
Debug.Log(s1 + ":" + s2);
调试结果为abc:123
那么为什么s1没有更新为123,因为s1和s2包含相同的引用,如果一个变量更新它,那么第二个变量将自动更新。
这是一个简单的代码片段,但让我有些困惑:
string s1 = "abc";
string s2 = s1;
s2 = "123";
Debug.Log(s1 + ":" + s2);
调试结果为abc:123
那么为什么s1没有更新为123,因为s1和s2包含相同的引用,如果一个变量更新它,那么第二个变量将自动更新。
这是对引用使用的一个常见误解。
s1是一个引用类型,但它的内容是一个值。你可以认为所有变量都是值类型,但编译器处理它们的方式因值或引用类型而异。
string s1 = "abc";
s1等于存储“abc”的地址,假设为0x0000AAAA。
string s2 = s1;
s2指向与s1相同的地址,因此其值与s1相同。两者的值都为0x000AAAA。
s2 = "123";
字符串是不可变的,这意味着您无法修改字符串,每当您分配新值或进行修改时,就会在内存中的其他位置创建一个新字符串,而先前的字符串则会准备好进行垃圾回收(如果需要,但这在我们的情况下并非如此)。此时,s1仍具有值0x0000AAAA,而s2具有新值0X0000BBBB。
Debug.Log(s1 + ":" + s2);
由于两个点引用不同的内容,因此它们打印不同的结果。
这只是一个引用类型,因为变量中包含的值并不意味着要直接使用,而是意味着将指针发送到存储实际对象的内存地址位置。
除非使用 out/ref (C++ 中的 &),否则暗示要使用变量的值(地址),最有可能作为参数。
请注意,这种行为对于任何对象都是相同的,而不仅仅是字符串。
Dog dogA = new Dog();
Dog dogB = dogA;
dogA.name = "Wolfie"; // Affect both since we are dereferencing
dogA = new Dog(); // dogA is new object, dogB is still Wolfie
编辑:原帖要求对ref/out进行解释。
当你想要改变一个对象时,你应该考虑以下几点:
void ChangeObject(GameObject objParam)
{
objParam = new GameObject("Eve");
}
void Start(){
GameObject obj = new GameObject("Adam");
ChangeObject(obj);
Debug.Log(obj.name); // Adam...hold on should be Eve (??!!)
}
ChangeObject以GameObject为参数,编译器将obj中包含的值(00000AAAA)复制到objParam中,并对其进行副本制作,两者现在具有相同的值。
在方法内,objParam被赋予一个新值,不再与方法外部的obj相关联。 objParam是该方法的局部变量,在完成后被删除(游戏对象仍然在场景中,但引用已丢失)。
如果您希望在方法内更改obj:
void ChangeObject(ref GameObject objParam)
{
objParam = new GameObject("Eve");
}
void Start(){
GameObject obj = new GameObject("Adam");
ChangeObject(ref obj);
Debug.Log(obj.name); // Yeah it is Eve !!!
}
这一次,传递的不是obj的值而是它的地址。因此,obj可能包含0x0000AAAA但其自身地址为0x0000AABB,则objParam的值现在为0x0000AABB并且更改objParam意味着更改存储在0x0000AABB处的值。
out和ref工作方式相同,只是out要求在方法内分配一个值,而ref可以在不影响给定参数的情况下离开该方法。
字符串是不可变的。当你赋值string s1 = "abc"
时,你将由字符串字面量abc
创建的新对象引用分配给s1的引用。
因此,当你赋值s2 = "123";
时,s2将引用由字符串字面量"123"
创建的新对象。
希望这可以帮助你!
string s1 = "abc";
abc
并将其赋值给s1
,这类似于:[假设Foo
是一个类];Foo fooObj= new Foo();
fooObj= new Foo(); // this will be a new instant
还有一点需要澄清; new Foo();
会在内部创建一个对象并分配给fooObj
,我们无法更改new Foo();
的值,因为它是在内部创建的,但我们可以处理fooObj
因为变量不是对象。变量是指向对象的引用
当你执行s2 = "123"
时,你重新分配了变量s2
所指向的引用(而不是它所指向的对象),将其指向一个不同的对象(一个类型为string
且值为123
的对象)
是的,string
是不可变的(正如其他人所指出的),但实际上这并不是这个问题的罪魁祸首。这种情况发生在可变和不可变类型以及值和引用类型之间。关键区别是你正在修改变量指向的引用(内存地址),而不是改变该内存地址的内容。
对于值类型:
int i1 = 0;
int i2 = i1;
i2 = 2;
Debug.Log(i1.ToString() + ":" + i2.ToString());
会记录日志:0:2
。
同时使用引用类型:
public class MyClass {
private string _cont;
public MyClass(string cont) { _cont = cont; }
public override string ToString() { return _cont; }
}
MyClass c1 = new MyClass("abc");
MyClass c2 = c1;
c2 = new MyClass("123");
Debug.Log(c1.ToString() + ":" + c2.ToString());
还会记录 abc:123
C# 中改变变量指向的内存地址实际内容只有两种方法:一种是使用 unsafe
指针(并且它们有自己的语法),另一种是将该变量作为 ref
或 out
参数传递给方法(必须明确执行)。