我在一个网站上找到了以下代码。
string a = "xx";
string b = "xx";
string c = "x";
string d = String.Intern(c + c);
Console.WriteLine((object)a == (object)b); // True
Console.WriteLine((object)a == (object)d); // True
在这里,将a、b、d再次转换为对象类型的目的是什么,既然它们本身就是字符串对象?
我在一个网站上找到了以下代码。
string a = "xx";
string b = "xx";
string c = "x";
string d = String.Intern(c + c);
Console.WriteLine((object)a == (object)b); // True
Console.WriteLine((object)a == (object)d); // True
在这里,将a、b、d再次转换为对象类型的目的是什么,既然它们本身就是字符串对象?
C#编译器会尝试在编译时获取所有常量字符串,这称为字符串驻留。因此,在代码生成之后,a
和b
是对包含"xx"的相同字符串的引用。
您可以通过比较它们的引用(将它们转换为对象并进行等式检查或使用object.ReferenceEquals
)来检查这一点。请记住,字符串的==
运算符比较它们的值而不是引用。
另一个需要提到的事情是,在.NET中字符串是不可变的。
string a = "xx";
string b = "x" + "x"; // String interning here
string c = string.Join("", new[] { "x", "x" }); // No interning here because it is evaluated at runtime
Console.WriteLine((object)a == (object)b); // True. Reference check
Console.WriteLine(a == b); // True. Value check
Console.WriteLine((object)a == c); //False. Reference check. Described below
Console.WriteLine(a == c); // True. Value check
为什么 Console.WriteLine((object)a == c);
进行引用检查?因为编译器会选择在 object 上的 ==
操作符来检查引用相等性。
所以在你的问题中将类型转换为 object 的整个目的是为了检查字符串常量池是否起作用。假设在编译时没有发生字符串常量池。
string a = "xx";
string b = "xx";
string c = "x";
string d = String.Intern(c + c);
那么Console.WriteLine((object)a == (object)b);
会输出"False",因为a
和b
是内存中两个不同的字符串引用,它们都看起来像"xx"。
a
也引用了与d
相同的字符串,因为d
是使用内联过程生成的。在运行时,通过使用string.Intern
检索对“xx”的引用,并将其分配给d
,因此a
和d
引用相同的内容。 - Hamid PourjamSystem.String类是.NET框架类库中提供的不可变引用类型。该类在任何字符串操作时内部创建一个新的字符串对象。该类型的对象的内容不会改变,尽管语法使其看起来好像内容可以改变。此外,字符串被用作哈希表键来计算哈希值,以避免破坏哈希数据结构的风险。
示例:
string a = "hello";
string b = "h";
// Append to contents of 'b'
b += "ello";
// When you set the variable's b value to "hello",
// this would result in changing the pointer
// to the object in the HEAP the variable "a" is already pointing to
// Result would be: (reference of a == reference of b) --> TRUE
// b = "hello";
Console.WriteLine(a == b); // value comparison
Console.WriteLine((object)a == (object)b); // reference comparison
Console.WriteLine (object.ReferenceEquals(a,b)); // reference comparison without casting
结果:
True
False
False
说明:
这将创建一个新对象:
string a = "hello";
string b = "h";
b += "ello";
string c = "hello";
Console.WriteLine (object.ReferenceEquals(a,c)); // --> TRUE
C#使用==
标记表示三种不同的运算符:可重载的相等检查运算符(可用于类类型或值类型,如果对于所涉及的确切类型存在重载),不可重载的引用标识检查运算符(它要求两个操作数都是类引用,并且要求类型不是互斥的),以及空检查运算符(它可用于任何类类型、可空值类型或可能是上述类型之一的泛型)。虽然大多数形式的重载在.NET语言中都有统一的定义方式,但一个运算符用于这三种相等性的情况是不一样的。其他语言如VB.NET使用不同的标记来表示第一种形式(例如,在VB.NET中,表达式(x = y)
如果定义了相等测试重载,则使用该重载,否则会生成语法错误;(x Is y)
测试是否标识了相同的对象实例,而不考虑是否存在重载的相等运算符)。
将类型转换为Object
的目的是确保==
标记被解释为代表引用标识检查运算符,而不是可重载的相等运算符;这种类型转换仅在C语言实现==
运算符的方式下需要,而在其他语言如VB.NET中则不需要。
使用代码进行评论
因此,如果在运行时将“xx”值全部设置为相同的值,则会得到不同的false结果,因为编译器没有机会在运行时进行优化,尽管在两种情况下都是相同的代码和输入值,但是预编译与运行时会产生不同的结果。
private void button1_Click(object sender, EventArgs e)
{
string a = "xx";
string b = "xx";
string c = "x";
string d = String.Intern(c + c);
Console.WriteLine((object)a == (object)b); // True
Console.WriteLine((object)a == (object)d); // True
}
private void button2_Click(object sender, EventArgs e)
{
string a = textBox1.Text; //type in xx at runtime
string b = textBox2.Text; //type in xx at runtime
string c = textBox3.Text; //type in just "x" at runtime
string d = String.Intern(c + c);
Console.WriteLine((object)a == (object)b); // False with runtime values that have the same value
Console.WriteLine((object)a == (object)d); // False
Console.WriteLine(a == d); // True - the Equals Operator of the string works as expected still
}
operator==(object, object)
(引用相等),而不是更适合重载分辨的operator==(string, string)
(值相等),因此需要进行类型转换。操作符调用在编译时解析,不具有多态性。另一种选择是在没有强制转换的情况下调用object.ReferenceEquals()
。 - Theodoros Chatzigiannakisstring e = c + c;
,那么使用“object”版本时,e
和a
不会相等,但是只使用 == 比较时,它们将相等。这是理解字符串池的关键点:它改变了引用比较,同时保持了值比较不变。 - Eric Lippert