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

?s 
"Category" 
?tvi.Header 
"Category" 
?s == tvi.Header 
false 
?s.Equals(tvi.Header) 
true 
?s == tvi.Header.ToString() 
true 

也就是s和tvi。标头包含“Category”,但==返回false, Equals()返回true。

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

有人能解释一下这是为什么吗?


当前回答

除了Jon Skeet的答案,我还想解释为什么大多数情况下,当使用==时,你实际上在不同的字符串实例上得到了具有相同值的答案:

string a = "Hell";
string b = "Hello";
a = a + "o";
Console.WriteLine(a == b);

如您所见,a和b必须是不同的字符串实例,但由于字符串是不可变的,因此运行时使用所谓的字符串实习来让a和b在内存中引用相同的字符串。对象的==操作符检查引用,由于a和b引用同一个实例,结果为真。当您更改其中任何一个时,将创建一个新的字符串实例,这就是为什么字符串实习是可能的。

顺便说一下,乔恩·斯基特的回答并不完整。的确,x == y是假的,但这只是因为他在比较对象,对象通过引用进行比较。如果你写(string)x == (string)y,它会再次返回true。字符串重载了它们的==-运算符,调用String。等于下面。

其他回答

两个差异:

Equals is polymorphic (i.e. it can be overridden, and the implementation used will depend on the execution-time type of the target object), whereas the implementation of == used is determined based on the compile-time types of the objects: // 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 Equals will throw an exception if you call it on null, == won't string x = null; string y = null; if (x.Equals(y)) // NullReferenceException if (x == y) // Yes

注意,使用object可以避免后者成为问题。等于:

if (object.Equals(x, y)) // Fine even if x or y is null

很明显,tvi。header不是一个字符串。==是一个由String类重载的操作符,这意味着只有当编译器知道操作符的两端都是String时,它才会工作。

c#有两个“等于”概念:equals和ReferenceEquals。对于您将遇到的大多数类,==操作符使用其中一个或另一个(或两者都使用),并且通常只在处理引用类型时测试ReferenceEquals(但字符串Class是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)

问题中出现的明显矛盾是由于在一种情况下Equals函数是在字符串对象上调用的,而在另一种情况下==操作符是在系统上调用的。对象类型。字符串和对象实现相等的方式不同(分别是值和引用)。

除此之外,任何类型都可以以不同的方式定义==和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

他接着说,double. equals (double)方法被设计用来正确地处理列表和字典。另一方面,==运算符是按照IEEE 754浮点类型标准设计的。

在确定字符串相等的特定情况下,行业偏好在大多数情况下既不使用==也不使用string. equals (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

这个例子中的两个字符串看起来是一样的("Café"),所以如果使用naïve(序数)相等,调试起来可能会非常困难。

TreeViewItem的Header属性静态类型为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