字符串如何在 C# 中工作?
How does string works in c#?
我知道字符串是不可变的,一旦创建我们就无法更改它,我读过如果我们创建一个新的字符串对象并为其分配一个值,然后在内部为同一个字符串对象分配另一个值实际上是创建并分配了新值的另一个对象。假设我有:
string str = "dog";
str = "cat";
如果我写Console.WriteLine(str);
它returnscat
。
那么内部有两个对象?但他们有相同的名字?它是如何工作的?我已经对 google 进行了一些研究,但我还没有找到对我来说足够有说服力的东西,所以我可以澄清我对此的想法。
我知道字符串是引用类型,所以我们在堆栈中有一个对象,它引用了堆中的一个值,在这种情况下发生了什么?(见上面的代码)。
我已经上传了一张图片,如果我对堆栈和堆的想法有误,请原谅我,这就是我问这个问题的原因。
图片是否反映了第一行代码(string str = "dog";
)中发生的事情?然后第二行代码应该发生什么?堆中的 dog
值发生变化?然后在堆栈中创建一个新对象来引用它?那么之前存在的对象会发生什么?他们有相同的名字吗?
很抱歉提出这么多问题,但我认为正确理解这一点并了解幕后发生的事情非常重要...
是的,有两个对象。不,他们没有相同的名字。尽量不要将变量视为对象本身 本身 的 "name" - 它更像是对象在内存中位置的临时名称。 (将变量视为对象的 "name" 有点误导的原因是您可以有多个变量引用同一个对象;对象本身并不是这样的 "names" ,或者有几个对象 - 这就是您恰好存储引用的方式)。
"string str" 最初引用了字符串 "dog." 将 "cat" 分配给 "str" 后,变量现在引用了字符串 "cat."
两个字符串仍然存在于内存中(至少是暂时的),但是 "dog" 字符串不再可访问,因为您没有对它的引用(因此不再 "know" 它的地点)。你事先不知道它们将在内存中存在多长时间,因为垃圾收集器可以随时从内存中删除 "dog" 字符串,因为不再有任何对它的引用。
顺便说一下,您关于堆栈上的值和对堆上对象的引用是正确的 - 这是一个很好的区别。
你很接近。你的图片准确地代表了第一行代码中发生的事情。但是,情况与您对第二行代码的描述略有不同。
对于行 str = "cat";
,在堆中创建了第二个字符串对象,并且 str
变量更改为引用该新对象。你剩下 str
指向 "cat"
和一个孤立的 "dog"
对象在堆上,没有对它的引用。
"dog"
对象可能会被垃圾收集器清除,因为没有对它的引用。
回顾 String Interning or .Net String Intern table or CLR Intern Pool.
基本上,公共语言运行时 (CLR) 维护 table 个 [唯一] 字符串值,每当您在代码中操作一个字符串时,CLR 都会检查这个实习生 table 以查看您是否使用了新值正在尝试创建是否已经存在。如果是,它只是重新分配您正在修改的变量以指向实习生池中的该条目。如果不是,它会将值添加到池中并 returns 该新引用。池中不再被变量引用的旧值被垃圾收集。
当你将str
赋值给"dog"时,它就像你在内存中描述的那样:引用变量str
现在是"pointing at"字符串的位置刚刚实例化。
str => MEMORY LOCATION "1": "dog"
MEMORY LOCATION "2":
MEMORY LOCATION "3":
当 str
被重新分配给您的新字符串 "cat" 时,它也在内存中创建,现在 str
被调整为指向 "cat"新位置。
MEMORY LOCATION "1": "dog"
str => MEMORY LOCATION "2": "cat"
MEMORY LOCATION "3":
"dog" 会怎样?现在实际上无法访问它,因为我们不再有对其位置的引用(在内存、堆中,术语在这种情况下可以互换)。稍后,当垃圾收集器检查要清理的内存时,它会意识到没有任何引用 "dog" 并且它会根据需要将内存标记为被删除和替换。
我知道字符串是不可变的,一旦创建我们就无法更改它,我读过如果我们创建一个新的字符串对象并为其分配一个值,然后在内部为同一个字符串对象分配另一个值实际上是创建并分配了新值的另一个对象。假设我有:
string str = "dog";
str = "cat";
如果我写Console.WriteLine(str);
它returnscat
。
那么内部有两个对象?但他们有相同的名字?它是如何工作的?我已经对 google 进行了一些研究,但我还没有找到对我来说足够有说服力的东西,所以我可以澄清我对此的想法。
我知道字符串是引用类型,所以我们在堆栈中有一个对象,它引用了堆中的一个值,在这种情况下发生了什么?(见上面的代码)。
我已经上传了一张图片,如果我对堆栈和堆的想法有误,请原谅我,这就是我问这个问题的原因。
图片是否反映了第一行代码(string str = "dog";
)中发生的事情?然后第二行代码应该发生什么?堆中的 dog
值发生变化?然后在堆栈中创建一个新对象来引用它?那么之前存在的对象会发生什么?他们有相同的名字吗?
很抱歉提出这么多问题,但我认为正确理解这一点并了解幕后发生的事情非常重要...
是的,有两个对象。不,他们没有相同的名字。尽量不要将变量视为对象本身 本身 的 "name" - 它更像是对象在内存中位置的临时名称。 (将变量视为对象的 "name" 有点误导的原因是您可以有多个变量引用同一个对象;对象本身并不是这样的 "names" ,或者有几个对象 - 这就是您恰好存储引用的方式)。
"string str" 最初引用了字符串 "dog." 将 "cat" 分配给 "str" 后,变量现在引用了字符串 "cat."
两个字符串仍然存在于内存中(至少是暂时的),但是 "dog" 字符串不再可访问,因为您没有对它的引用(因此不再 "know" 它的地点)。你事先不知道它们将在内存中存在多长时间,因为垃圾收集器可以随时从内存中删除 "dog" 字符串,因为不再有任何对它的引用。
顺便说一下,您关于堆栈上的值和对堆上对象的引用是正确的 - 这是一个很好的区别。
你很接近。你的图片准确地代表了第一行代码中发生的事情。但是,情况与您对第二行代码的描述略有不同。
对于行 str = "cat";
,在堆中创建了第二个字符串对象,并且 str
变量更改为引用该新对象。你剩下 str
指向 "cat"
和一个孤立的 "dog"
对象在堆上,没有对它的引用。
"dog"
对象可能会被垃圾收集器清除,因为没有对它的引用。
回顾 String Interning or .Net String Intern table or CLR Intern Pool.
基本上,公共语言运行时 (CLR) 维护 table 个 [唯一] 字符串值,每当您在代码中操作一个字符串时,CLR 都会检查这个实习生 table 以查看您是否使用了新值正在尝试创建是否已经存在。如果是,它只是重新分配您正在修改的变量以指向实习生池中的该条目。如果不是,它会将值添加到池中并 returns 该新引用。池中不再被变量引用的旧值被垃圾收集。
当你将str
赋值给"dog"时,它就像你在内存中描述的那样:引用变量str
现在是"pointing at"字符串的位置刚刚实例化。
str => MEMORY LOCATION "1": "dog"
MEMORY LOCATION "2":
MEMORY LOCATION "3":
当 str
被重新分配给您的新字符串 "cat" 时,它也在内存中创建,现在 str
被调整为指向 "cat"新位置。
MEMORY LOCATION "1": "dog"
str => MEMORY LOCATION "2": "cat"
MEMORY LOCATION "3":
"dog" 会怎样?现在实际上无法访问它,因为我们不再有对其位置的引用(在内存、堆中,术语在这种情况下可以互换)。稍后,当垃圾收集器检查要清理的内存时,它会意识到没有任何引用 "dog" 并且它会根据需要将内存标记为被删除和替换。