string.Equals()和==运算符是否真的相同?

mil*_*liu 224 c# string

他们真的一样吗?今天,我遇到了这个问题.这是立即窗口的转储:

?s 
"Category" 
?tvi.Header 
"Category" 
?s == tvi.Header 
false 
?s.Equals(tvi.Header) 
true 
?s == tvi.Header.ToString() 
true 
Run Code Online (Sandbox Code Playgroud)

所以,无论是stvi.Header包含"类别",而是==返回false,并Equals()返回true.

s被定义为字符串,tvi.Header实际上是一个WPF TreeViewItem.Header.那么,他们为什么会回归不同的结果呢?我一直认为它们可以在C#中互换.

任何人都可以解释为什么会这样吗?

Jon*_*eet 342

两个区别:

  • Equals是多态的(即它可以被覆盖,并且使用的实现将取决于目标对象的执行时类型),而==used 的实现是基于对象的编译时类型确定的:

    // Avoid getting confused by interning
    object x = new StringBuilder("hello").ToString();
    object y = new StringBuilder("hello").ToString();
    if (x.Equals(y)) // Yes
    
    // The compiler doesn't know to call ==(string, string) so it generates
    // a reference comparision instead
    if (x == y) // No
    
    string xs = (string) x;
    string ys = (string) y;
    
    // Now *this* will call ==(string, string), comparing values appropriately
    if (xs == ys) // Yes
    
    Run Code Online (Sandbox Code Playgroud)
  • Equals 如果你在null上调用它将会爆炸,==不会

    string x = null;
    string y = null;
    
    if (x.Equals(y)) // Bang
    
    if (x == y) // Yes
    
    Run Code Online (Sandbox Code Playgroud)

请注意,使用以下方法可以避免后者出现问题object.Equals:

if (object.Equals(x, y)) // Fine even if x or y is null
Run Code Online (Sandbox Code Playgroud)

  • `x == y`等于false,因为您正在检查与对象类的相等运算符的引用相等性.`(字符串)x ==(字符串)y`实际上返回true,至少在.Net 4.0中. (4认同)
  • 很好的答案.使用`String.Equals(x,y)`代替`object.Equals(x,y)`来避免null问题会不会更有意义? (4认同)
  • @Jon Skeet我指的是那种类型检查比`object.Equals(x,y)`更有优势.问题是关于字符串比较,所以似乎添加的类型检查将使用`String.Equals()`而不是`object.Equals()`.是否有理由避免类型检查会更好?另外,感谢您回答对一年多的问题的评论! (3认同)
  • @Chaulky:没有特别的原因 - 除了我知道`object.Equals`总是*可用于避免无效问题,而对于特定类型我需要检查文档:) (2认同)

Jef*_*dge 61

问题中出现的明显矛盾是由于在一种情况下Equals在一个string对象上调用该函数,而在另一种情况下==是在该System.Object类型上调用该运算符.string并且object彼此不同地实现相等(分别为值与参考).

除此之外,任何类型都可以定义==Equals不同,因此通常它们不可互换.

这是一个使用的例子double(来自Joseph Albahari的C#语言规范§7.9.2的注释):

double x = double.NaN;
Console.WriteLine (x == x);         // False
Console.WriteLine (x != x);         // True
Console.WriteLine (x.Equals(x));    // True
Run Code Online (Sandbox Code Playgroud)

他接着说,该double.Equals(double)方法旨在与列表和词典一起正常工作.该==运营商,在另一方面,被设计为遵循浮点类型的IEEE 754标准.

在确定字符串相等性的特定情况下,行业偏好既不使用==也不使用string.Equals(string)大部分时间.这些方法确定两个字符串是否是相同的字符,这很少是正确的行为.最好使用string.Equals(string, StringComparison),它允许您指定特定类型的比较.通过使用正确的比较,您可以避免很多潜在的(非常难以诊断)错误.

这是一个例子:

string one = "Caf\u00e9";        // U+00E9 LATIN SMALL LETTER E WITH ACUTE
string two = "Cafe\u0301";       // U+0301 COMBINING ACUTE ACCENT
Console.WriteLine(one == two);                                          // False
Console.WriteLine(one.Equals(two));                                     // False
Console.WriteLine(one.Equals(two, StringComparison.InvariantCulture));  // True
Run Code Online (Sandbox Code Playgroud)

这个例子中的两个字符串看起来都一样("Café"),所以如果使用天真(序数)相等,这可能非常难以调试.


pal*_*wim 44

C#有两个"等于"的概念:EqualsReferenceEquals.对于您将遇到的大多数类,==运算符使用一个或另一个(或两者),并且通常仅ReferenceEquals在处理引用类型时进行测试(但是stringClass是C#已经知道如何测试值相等的实例).

  • Equals比较值.(即使int内存中的同一个点中不存在两个单独的变量,它们仍然可以包含相同的值.)
  • ReferenceEquals 比较引用并返回操作数是否指向内存中的同一对象.

示例代码:

var s1 = new StringBuilder("str");
var s2 = new StringBuilder("str");
StringBuilder sNull = null;

s1.Equals(s2); // True
object.ReferenceEquals(s1, s2); // False
s1 == s2 // True - it calls Equals within operator overload
s1 == sNull // False
object.ReferenceEquals(s1, sNull); // False
s1.Equals(sNull); // Nono!  Explode (Exception)
Run Code Online (Sandbox Code Playgroud)


Dir*_*mar 16

静态类型的Header属性TreeViewItem是类型object.

因此==产量false.您可以使用以下简单代码重现此内容:

object s1 = "Hallo";

// don't use a string literal to avoid interning
string s2 = new string(new char[] { 'H', 'a', 'l', 'l', 'o' });

bool equals = s1 == s2;         // equals is false
equals = string.Equals(s1, s2); // equals is true
Run Code Online (Sandbox Code Playgroud)

  • 值得注意的是,s1 == s2变为s1.Equals(s2)IFF s1和s2都声明为字符串。字符串相等在C#中具有特殊含义。 (2认同)

Dan*_*ker 5

除了Jon Skeet 的回答之外,我还想解释一下为什么大多数时候使用时==您实际上会在true具有相同值的不同字符串实例上获得答案:

string a = "Hell";
string b = "Hello";
a = a + "o";
Console.WriteLine(a == b);
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,a而且b必须是不同的字符串实例,但因为字符串是不可变的,运行时使用所谓的字符串实习,让双方ab引用相同字符串在内存中。==对象的运算符检查引用,并且由于ab引用同一个实例,结果是true。当您更改其中任何一个时,都会创建一个新的字符串实例,这就是可以使用字符串实习的原因。

顺便说一下,Jon Skeet 的回答并不完整。确实,x == yfalse但那只是因为他正在比较对象和对象通过引用进行比较。如果你写(string)x == (string)y,它会true再次返回。所以字符串有它们的 == 运算符重载,它String.Equals在下面调用。

  • 我认为从答案中已经足够明显了,但我已经对其进行了编辑以使其更加完整。但是,您对实习的回答*不*正确。`a` 和 `b` 指的是上面*不同的*实例;字符串实习仅适用于编译时常量。您的代码打印 True 是因为它调用了 == 重载,它正在比较字符序列。`a` 最终引用的字符串 *not* 已被插入。 (10认同)
  • @Ax。:虽然系统可能会在创建字符串时检查它是否在实习池中,而不尝试在它不在的情况下添加它,但在大多数情况下创建的绝大多数字符串程序不会在实习生池中。如果可能存在它的副本,则根据实习池检查每个生成的字符串通常会浪费更多的时间而不是节省的时间。 (3认同)
  • @Ax。:系统仅在明确请求时才对非常量字符串进行实习,部分原因是一旦字符串被实习,实习副本就永远不会被垃圾收集。如果碰巧经常使用包含 32,000 个字符的特定序列的字符串,则对字符串进行实习可能会为否则会创建的每个副本节省 64K。但是,如果一个 32,000 个字符串的实习生不再使用该字符序列,则将永久浪费 64K 的内存。程序不能多次这样做。 (2认同)