C# 字符串是引用类型,但当它';s的作业已更新
这是一个简单的代码片段,让我有点困惑:C# 字符串是引用类型,但当它';s的作业已更新,c#,string,C#,String,这是一个简单的代码片段,让我有点困惑: string s1 = "abc"; string s2 = s1; s2 = "123"; Debug.Log(s1 + ":" + s2); 调试结果为abc:123 那么为什么s1不更新为123,因为s1和s2包含相同的引用,如果一个变量更新它,那么第二个变量将自动更新 因为变量不是对象。变量是对对象的引用 执行s2=“123”操作时,将变量s2指向的引用(而不是它指向的对象)重新指定给不同的对象(值为123的字符串类型的对象) 是的,string
string s1 = "abc";
string s2 = s1;
s2 = "123";
Debug.Log(s1 + ":" + s2);
调试结果为abc:123
那么为什么s1不更新为123,因为s1和s2包含相同的引用,如果一个变量更新它,那么第二个变量将自动更新 因为变量不是对象。变量是对对象的引用
执行s2=“123”
操作时,将变量s2
指向的引用(而不是它指向的对象)重新指定给不同的对象(值为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#中,只有两种方法可以更改变量指向的内存地址的实际内容:一种是使用不安全的指针(它们有自己的语法),另一种是将该变量作为ref
或out
参数传递给方法(这需要显式完成)这是对参考文献使用的常见误解
s1是引用类型,但其内容是值。您可以认为所有变量都是值类型,但编译器处理它们的方式因值或引用类型而异
string s1 = "abc";
s1等于存储“abc”的地址,比如0x0000AAAA
string s2 = s1;
s2指向与s1相同的地址,因此其值与s1相同。两者的值均为0x000AAAA
s2 = "123";
字符串是不可变的,这意味着您不能修改字符串,无论何时分配新值或进行修改,您都会在内存中的其他位置创建一个新字符串,而前一个字符串将准备好用于GC(如果需要的话)(实际上不是在我们的示例中)。此时,s1仍然具有值0x0000AAAA,而s2具有新的值0x0000BBBBB
Debug.Log(s1 + ":" + s2);
因为两个点的内容不同,所以打印的结果也不同
它只是一种引用类型,因为变量中包含的值不是要按原样使用,而是要将指针发送到内存中存储实际对象的地址位置
除非您使用out/ref(C++中的&in),否则这意味着将使用变量的值(地址),很可能是作为参数
请注意,此行为与任何对象相同,而不仅仅是字符串
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的工作原理相同,只是out要求在方法中指定一个值,而ref可以在不影响给定参数的情况下离开方法。字符串是不可变的。
当您分配string s1=“abc”
时,您正在将string literalabc
创建的新对象引用分配给s1的引用。
因此,当您分配s2=“123”代码>,s2将引用由字符串literal创建的新对象“123”
希望这有帮助 通过将字符串文本分配给字符串变量,实际上是实例化一个新的字符串对象并将其分配给现有的字符串对象。即
如果我们声明并初始化s1
,如下所示
string s1 = "abc";
实际上,我们正在创建一个新的字符串对象abc
,并分配给s1
,这类似于:[假设Foo
是一个类]
Foo fooObj= new Foo();
fooObj= new Foo(); // this will be a new instant
还有一点需要澄清<代码>新Foo()
将在内部创建一个对象并分配给fooObj
我们无法更改new Foo()的值代码>因为它是内部创建的,但是我们可以处理fooObj
字符串是immutable@un-幸运的是,问题的标题是相同的,但问题的内容是不同的。在字符串是否不可变(或者它们是引用或值类型)对这个问题不重要之前,我已经检查过了。这个问题是关于变量的,不是关于它们的内容。回答这个问题很好,但fafse已经说过了几乎相同的话。Thankscan u deine more“除非您使用out/ref(C++中的&),否则这意味着变量的值将被使用(地址),很可能是一个参数。”