[英]How strings are created and managed in CLR?
我和我的朋友正在讨论 Do.net 框架中的字符串,它们如何是引用类型但表现得像值类型(不可变)。 我们都知道字符串是 CLR 内部的,但我们并没有真正在简短的讨论中得出结论,字符串是如何由 CLR/Framework 创建和管理的。
例如,在下面的代码中,显然s1
和s2
是不同的实例,但是正如您看到的,当我s2.ToUpper()
时,结果返回到s1
。
public static void Main (string[] args)
{
string s1 = "HELLO";
string s2 = "hello";
Console.WriteLine (s1.GetHashCode()); //Prints 68624562
Console.WriteLine (s2.GetHashCode()); //Prints 99162322
Console.WriteLine (s2.ToUpper().GetHashCode()); //Prints 68624562 too!
}
因此,问题在于调用s2.ToUpper()
时 CLR 是否创建了新字符串"HELLO"
并检查它是否已经存在,如果是则丢弃新创建的字符串? 有人可以解释这里的魔法吗?
String.GetHashCode() 生成一个基于字符串内容的 hash 值。 因此,相同的字符串生成相同的 hash 是完全自然的。这意味着您无法断定 ToUpper() 返回的字符串引用必须与 s1 引用匹配。 事实并非如此,实施起来太昂贵了。
您可以通过测试此代码来验证:
static void Main(string[] args) {
var s1 = "hello";
var s2 = "HELLO";
var s3 = s1.ToUpper();
bool eq = object.ReferenceEquals(s2, s3);
System.Diagnostics.Debug.Assert(!eq);
}
对于相同的输入,两次GetHashCode()
调用给出相同的结果也就不足为奇了,这就是散列的意义所在……
相反,当你这样做时:
Console.WriteLine(Object.ReferenceEquals(s2.ToUpper(), s1));
它只是返回false
。 因此,您实际上有两个string
实例,它们都具有相同的内容。
我认为您需要复习有关散列、散列码和相等性的知识。
或者您来自 Java? 也许您的印象是 hash 代码与 object 参考值相关,因为Object.getHashCode()
的文档指出:
在相当实用的情况下,class Object 定义的 hashCode 方法确实会为不同的对象返回不同的整数。 (这通常是通过将 object 的内部地址转换为 integer 来实现的,但是 JavaTM 编程语言不需要这种实现技术。)
您不能使用GetHashCode()
来唯一标识实例。 对于具有相同值的两个不同对象,hash 代码必须相同。 否则它不会作为 hash 代码工作。
s2.ToUpper() 只是方法调用,不会更改 s2 object 的值(s2 是 String 类型的 object)。 它采用 s2 的值并返回具有“HELLO”值(ToUpper() 方法的结果)的 String class 的新实例。 在Main function的scope中还有两个对象s1和s2,它们的值保持不变。
要添加,请回答您的另一部分...
如果您还检查此Object.ReferenceEquals(s2.ToUpper(), s2)
您会发现它也是错误的。
字符串是immutable
的——这意味着在这种情况下ToUpper()
返回一个新实例。
所以答案是肯定的,“HELLO”是新字符串。
但是,正如其他人所说, GetHashCode()
只是一个“哈希值”——它主要用于在处理哈希和字典时使用不同的算法来“填充桶”。
或者查看此链接什么是覆盖 System.Object.GetHashCode 的最佳算法? - 和答案 - 让您很好地了解 hash 算法的工作原理 - 为什么它不是唯一的 - 以及为什么对于具有相同内容的字符串它可能相同。
或者这个http://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.