这是一个简单的代码片段,这让我有点困惑:
string s1 = "abc";
string s2 = s1;
s2 = "123";
Debug.Log(s1 + ":" + s2);
调试结果为abc:123
那么为什么s1没有更新为123,因为 s1和s2包含相同的引用,如果一个变量更新它,第二个将自动更新。
答案 0 :(得分:6)
这是对使用参考文献的常见误解。
s1是引用类型,但其内容是一个值。您可以认为所有变量都是值类型,但编译器处理它们的方式因值或引用类型而异。
string s1 = "abc";
s1等于" abc"存储,让我们说0x0000AAAA
string s2 = s1;
s2指向与s1相同的地址,因此其值与s1相同。两者都具有值0x000AAAA
s2 = "123";
字符串是不可变的,这意味着你不能修改字符串,无论何时你分配一个新值或修改,你在内存中的其他地方创建一个新的字符串,而前一个字符串准备好GC,如果需要的话(不是我们的情况)其实)。此时,s1仍然具有值0x0000AAAA,而s2具有新的0X0000BBBB。
Debug.Log(s1 + ":" + s2);
由于两个点的内容不同,因此会打印出不同的结果。
它只是一个引用类型,因为变量中包含的值不是按原样使用,而是用于将指针发送到存储实际对象的内存中的地址位置。
除非你使用out / ref(& in 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
编辑:OP需要对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获取GameObejct作为参数,编译器将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的地址。因此obj可能包含0x0000AAAA,但它自己的地址是0x0000AABB,那么objParam的值现在是0x0000AABB,更改objParam意味着更改存储在0x0000AABB的值。
out和ref的工作原理相同,只有那个需要在方法中分配一个值,而ref可以离开方法而不影响给定的参数。
答案 1 :(得分:1)
字符串是不可变的。
分配string s1 = "abc"
时,您将由字符串文字abc
创建的新对象引用分配给s1的引用。
因此,当您指定s2 = "123";
时,s2将引用由字符串文字"123"
创建的新对象。
希望这会有所帮助!!
答案 2 :(得分:1)
通过将字符串文字分配给String变量,实际上是实例化一个新的字符串对象并分配给现有的字符串对象。即,
如果我们声明并初始化s1
,如下所示;
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
答案 3 :(得分:0)
因为变量不是对象。变量是引用到对象
执行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
参数(需要明确完成)。