C# 如何在CLR中创建和管理字符串?

C# 如何在CLR中创建和管理字符串?,c#,string,clr,C#,String,Clr,我和我的朋友讨论了Dotnet框架中的字符串,它们是如何作为引用类型的,但其行为却像值类型(不可变)。我们都知道字符串是CLR内部的,但在那个简短的讨论中,我们并没有真正得出结论,即CLR/Framework是如何创建和管理字符串的 例如,在下面的代码中,s1和s2显然是不同的实例,但正如我执行s2.ToUpper()时所看到的,结果引用回s1 public static void Main (string[] args) { string s1 = "HELLO

我和我的朋友讨论了Dotnet框架中的字符串,它们是如何作为引用类型的,但其行为却像值类型(不可变)。我们都知道字符串是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”
,并检查它是否已经存在,如果是,则扔掉新创建的字符串?有人能解释一下这里的神奇之处吗?

您不能使用
GetHashCode()
来唯一标识实例。对于具有相同值的两个不同对象,哈希代码必须相同。否则它就不能作为哈希代码使用。

您不能使用
GetHashCode()
来唯一标识实例。对于具有相同值的两个不同对象,哈希代码必须相同。否则它就不能作为哈希代码工作。

String.GetHashCode()生成一个基于字符串内容的哈希值。因此,相同的字符串生成相同的散列是完全自然的。这意味着您不能断定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()生成一个基于字符串内容的哈希值。因此,相同的字符串生成相同的散列是完全自然的。这意味着您不能断定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
实例存在,它们都具有相同的内容

我认为你需要复习一下关于散列、散列代码和等式的知识

还是你来自爪哇?可能您得到的印象是哈希代码与对象引用值相关,因为
object.getHashCode()
的文档说明:

只要是合理可行的,类对象定义的hashCode方法确实会为不同的对象返回不同的整数(这通常通过将对象的内部地址转换为整数来实现,但JavaTM编程语言不需要这种实现技术。)


两个
GetHashCode()
调用对相同的输入给出相同的结果并不奇怪,这就是散列的要点

相反,当您这样做时:

Console.WriteLine(Object.ReferenceEquals(s2.ToUpper(), s1));
它只返回
false
。因此,实际上有两个
string
实例存在,它们都具有相同的内容

我认为你需要复习一下关于散列、散列代码和等式的知识

还是你来自爪哇?可能您得到的印象是哈希代码与对象引用值相关,因为
object.getHashCode()
的文档说明:

只要是合理可行的,类对象定义的hashCode方法确实会为不同的对象返回不同的整数(这通常通过将对象的内部地址转换为整数来实现,但JavaTM编程语言不需要这种实现技术。)

ToUpper()只是一个方法调用,它不会改变s2对象的值(s2是String类型的对象)。它接受s2的值,并返回字符串类的新实例和“HELLO”值(ToUpper()方法的结果)。 在主功能范围内,仍然有两个对象s1和s2,它们的值保持不变

s2.ToUpper()只是一个方法调用,它不会改变s2对象的值(s2是String类型的对象)。它接受s2的值,并返回字符串类的新实例和“HELLO”值(ToUpper()方法的结果)。
在主功能范围内,仍然有两个对象s1和s2,它们的值保持不变

要添加,请回答您的另一部分

如果您还检查了这个
Object.ReferenceEquals(s2.ToUpper(),s2)
,您将看到它也是false

字符串是
不可变的
——在本例中意味着
ToUpper()
返回一个新实例

所以答案是肯定的,“HELLO”是新字符串

但是,正如其他人所说,
GetHashCode()
只是一个“散列值”——它主要用于在处理散列和字典时使用不同的算法来“填充存储桶”

或者看看这个链接——以及答案——让你很好地了解散列算法是如何工作的——为什么它不是唯一的——以及为什么对于相同内容的字符串它可能是相同的


或者这一部分要添加,请回答您的另一部分

如果您还检查了这个
Object.ReferenceEquals(s2.ToUpper(),s2)
,您将看到它也是false

字符串是
不可变的
——在本例中意味着
ToUpper()
返回一个新实例

所以答案是肯定的,“HELLO”是新字符串

但是,正如其他人所说,
GetHashCode()
只是一个“散列值”——它主要用于在处理散列和字典时使用不同的算法来“填充存储桶”

或者看看这个链接——以及答案——让你很好地了解散列算法是如何工作的——为什么它不是唯一的——以及为什么对于相同内容的字符串它可能是相同的

或者这个

不同的字符串实例