将数据类型转换为“object”的目的是什么?

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


1
绝对没有。System.String 继承自 System.Object。 - misha130
1
不需要强制转换... - user6522773
27
那段代码的作者想要调用operator==(object, object)(引用相等),而不是更适合重载分辨的operator==(string, string)(值相等),因此需要进行类型转换。操作符调用在编译时解析,不具有多态性。另一种选择是在没有强制转换的情况下调用object.ReferenceEquals() - Theodoros Chatzigiannakis
类型系统的破坏。 - cat
7
该网站缺少能够解释正在发生的事情的代码;即如果我们有 string e = c + c; ,那么使用“object”版本时,ea 不会相等,但是只使用 == 比较时,它们将相等。这是理解字符串池的关键点:它改变了引用比较,同时保持了比较不变。 - Eric Lippert
4个回答

24

C#编译器会尝试在编译时获取所有常量字符串,这称为字符串驻留。因此,在代码生成之后,ab是对包含"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",因为ab是内存中两个不同的字符串引用,它们都看起来像"xx"。


手动构造字符串时,是否需要显式地进行内部化?(就像问题中所述) - Paul Stelian
3
"Operator =="(http://referencesource.microsoft.com/#mscorlib/system/string.cs,661)用于字符串的值检查。@Carl" - Hamid Pourjam
@dotctor 但是根据您的解释,在这种情况下,a的引用等于b的引用,这是否意味着更改b的值应该导致更改a的值?String是一种引用类型,并且它具有对堆中对象的引用。 - Legends
字符串在.NET中是不可变的。因此,您无法更改它们的值。@Legends - Hamid Pourjam
@dotctor。语句Console.WriteLine((object)a == (object)b)如何输出true?因为这里我没有将a赋值给b。那么a和b的引用怎么可能相等呢? - babybob
1
编译器在编译时使用字符串内联过程来完成。a也引用了与d相同的字符串,因为d是使用内联过程生成的。在运行时,通过使用string.Intern检索对“xx”的引用,并将其分配给d,因此ad引用相同的内容。 - Hamid Pourjam

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"就可以被垃圾回收了。

2
不应提到堆或栈,因为它们是.NET运行时的实现细节,可能在不同版本之间有所变化。[堆和栈] (https://blogs.msdn.microsoft.com/ericlippert/2010/09/30/the-truth-about-value-types/) - Hamid Pourjam
我的意思是“在CLR上实现的C#的微软实现”。这只是对你的回答的补充,也许可以使它更容易理解;-) - Legends

5

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

将类型转换为Object的目的是确保==标记被解释为代表引用标识检查运算符,而不是可重载的相等运算符;这种类型转换仅在C语言实现==运算符的方式下需要,而在其他语言如VB.NET中则不需要。


1

使用代码进行评论

因此,如果在运行时将“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 
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接