19

我在一个网站上找到了如下代码。

 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 本身就是字符串的对象,那么再次转换为对象类型的目的是什么?

4

4 回答 4

24

C# 编译器将尝试在编译时获取所有常量字符串。这称为字符串实习。因此,在生成代码之后,a并且b是对包含“xx”的相同字符串的引用。

您可以通过比较它们的引用来检查这一点(将它们转换为 object 并进行相等性检查或 use 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);要做参考检查呢?因为编译器将选择==检查引用相等性的对象上的运算符。


因此,在您的问题中转换为对象的全部目的是检查字符串实习是否有效。假设在编译时没有实习

 string a = "xx";
 string b = "xx";
 string c = "x";
 string d = String.Intern(c + c);

然后Console.WriteLine((object)a == (object)b);会打印“False”,因为ab是内存中两个不同字符串的引用,它们看起来都像“xx”。

于 2016-07-02T09:28:58.057 回答
6

提供的答案的补充: 字符串(C# 参考)

System.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";

以下将创建对现有对象的引用,更准确地说,它将指向变量“a”指向的同一对象→“hello”。

string c = "hello"; 
Console.WriteLine (object.ReferenceEquals(a,c)); // --> TRUE

字符串是不可变的——字符串对象的内容在对象创建后不能更改,尽管语法使它看起来好像您可以这样做。例如,当您编写此代码时,编译器实际上会创建一个新的字符串对象来保存新的字符序列,并将该新对象分配给 b。然后字符串“h”就可以进行垃圾回收了。

于 2016-07-02T10:57:46.207 回答
5

C# 使用该==标记来表示三个不同的运算符:一个可重载的相等检查运算符(如果所讨论的确切类型存在重载,则可用于类类型或值类型),一个不可重载的引用标识检查运算符(要求两者操作数是类引用,并且要求类型不是互斥的)和一个空检查运算符(可用于任何类类型、可为空值类型或可能是上述之一的泛型)。虽然大多数形式的重载是在 .NET 语言中以统一的方式定义的,但对所有三种相等性使用一个运算符却不是。其他语言如 VB.NET 对第一种形式使用不同的标记(例如,在 VB.NET 中,表达式(x = y)如果定义了相等测试重载,则使用相等测试重载,否则生成语法错误;(x Is y)测试是否xy标识相同的对象实例,而不考虑是否存在重载的相等运算符)。

转换为的目的Object是确保==令牌被解释为表示引用身份检查运算符而不是可重载的相等运算符;仅因为 C 实现==运算符的方式才需要此类转换,而在其他语言(如 VB.NET)中不需要。

于 2016-07-02T15:27:07.220 回答
1

用代码评论

因此,如果在运行时将值“xx”全部设置为相同的“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 
}
于 2016-07-02T20:42:58.703 回答