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

?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#中是可以互换的。

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


当前回答

对象由OBJECT_ID定义,OBJECT_ID是唯一的。如果A和B是对象 A == B为真,那么它们是相同的对象,它们有相同的数据和方法,但是,这也是成立的:

A.object_id == b.object_id

如果 A = (B)为真,这意味着两个对象处于相同的状态,但这并不意味着A和B完全相同。

字符串是对象。

请注意==和Equals操作符是自反的、simetric的、tranzitive的,因此它们是等价关系(使用关系代数术语)。

这意味着: 如果A, B和C是对象,则:

(1) A == A总是正确的;A. equals (A)总是正确的(反身性)

(2)如果A == B那么B == A;如果A等于(B)那么B等于(A) (simetry)

(3)如果A == B且B == C,则A == C;如果a .等于(B), B.等于(C),则a .等于(C)(静止性)

另外,你可以注意到这也是真的:

(A == B) => (A = (B)),但反过来就不对了。

A B =>
0 0 1
0 1 1
1 0 0
1 1 1

Example of real life: Two Hamburgers of the same type have the same properties: they are objects of the Hamburger class, their properties are exactly the same, but they are different entities. If you buy these two Hamburgers and eat one, the other one won't be eaten. So, the difference between Equals and ==: You have hamburger1 and hamburger2. They are exactly in the same state (the same weight, the same temperature, the same taste), so hamburger1.Equals(hamburger2) is true. But hamburger1 == hamburger2 is false, because if the state of hamburger1 changes, the state of hamburger2 not necessarily change and vice versa.

如果你和一个朋友同时得到一个汉堡,这是你的和他的,那么你必须决定把汉堡分成两部分,因为你gethamburger () == friend. gethamburger()为真,如果这种情况发生:friend. eathamburger(),那么你的汉堡也会被吃掉。

我可以写关于Equals和==的其他细微差别,但我有点饿了,所以我得走了。

此致敬意 拉约什·阿帕德。

其他回答

这里有很多描述性的答案,所以我不打算重复已经说过的内容。我想添加的是下面的代码,演示了我能想到的所有排列。由于组合的数量,代码相当长。您可以将其放到MSTest中,自己查看输出(输出包含在底部)。

这个证据支持了乔恩·斯基特的答案。

代码:

[TestMethod]
public void StringEqualsMethodVsOperator()
{
    string s1 = new StringBuilder("string").ToString();
    string s2 = new StringBuilder("string").ToString();

    Debug.WriteLine("string a = \"string\";");
    Debug.WriteLine("string b = \"string\";");

    TryAllStringComparisons(s1, s2);

    s1 = null;
    s2 = null;

    Debug.WriteLine(string.Join(string.Empty, Enumerable.Repeat("-", 20)));
    Debug.WriteLine(string.Empty);
    Debug.WriteLine("string a = null;");
    Debug.WriteLine("string b = null;");

    TryAllStringComparisons(s1, s2);
}
private void TryAllStringComparisons(string s1, string s2)
{
    Debug.WriteLine(string.Empty);
    Debug.WriteLine("-- string.Equals --");
    Debug.WriteLine(string.Empty);
    Try((a, b) => string.Equals(a, b), s1, s2);
    Try((a, b) => string.Equals((object)a, b), s1, s2);
    Try((a, b) => string.Equals(a, (object)b), s1, s2);
    Try((a, b) => string.Equals((object)a, (object)b), s1, s2);

    Debug.WriteLine(string.Empty);
    Debug.WriteLine("-- object.Equals --");
    Debug.WriteLine(string.Empty);
    Try((a, b) => object.Equals(a, b), s1, s2);
    Try((a, b) => object.Equals((object)a, b), s1, s2);
    Try((a, b) => object.Equals(a, (object)b), s1, s2);
    Try((a, b) => object.Equals((object)a, (object)b), s1, s2);

    Debug.WriteLine(string.Empty);
    Debug.WriteLine("-- a.Equals(b) --");
    Debug.WriteLine(string.Empty);
    Try((a, b) => a.Equals(b), s1, s2);
    Try((a, b) => a.Equals((object)b), s1, s2);
    Try((a, b) => ((object)a).Equals(b), s1, s2);
    Try((a, b) => ((object)a).Equals((object)b), s1, s2);

    Debug.WriteLine(string.Empty);
    Debug.WriteLine("-- a == b --");
    Debug.WriteLine(string.Empty);
    Try((a, b) => a == b, s1, s2);
#pragma warning disable 252
    Try((a, b) => (object)a == b, s1, s2);
#pragma warning restore 252
#pragma warning disable 253
    Try((a, b) => a == (object)b, s1, s2);
#pragma warning restore 253
    Try((a, b) => (object)a == (object)b, s1, s2);
}
public void Try<T1, T2, T3>(Expression<Func<T1, T2, T3>> tryFunc, T1 in1, T2 in2)
{
    T3 out1;

    Try(tryFunc, e => { }, in1, in2, out out1);
}
public bool Try<T1, T2, T3>(Expression<Func<T1, T2, T3>> tryFunc, Action<Exception> catchFunc, T1 in1, T2 in2, out T3 out1)
{
    bool success = true;
    out1 = default(T3);

    try
    {
        out1 = tryFunc.Compile()(in1, in2);
        Debug.WriteLine("{0}: {1}", tryFunc.Body.ToString(), out1);
    }
    catch (Exception ex)
    {
        Debug.WriteLine("{0}: {1} - {2}", tryFunc.Body.ToString(), ex.GetType().ToString(), ex.Message);
        success = false;
        catchFunc(ex);
    }

    return success;
}

输出:

string a = "string";
string b = "string";

-- string.Equals --

Equals(a, b): True
Equals(Convert(a), b): True
Equals(a, Convert(b)): True
Equals(Convert(a), Convert(b)): True

-- object.Equals --

Equals(a, b): True
Equals(Convert(a), b): True
Equals(a, Convert(b)): True
Equals(Convert(a), Convert(b)): True

-- a.Equals(b) --

a.Equals(b): True
a.Equals(Convert(b)): True
Convert(a).Equals(b): True
Convert(a).Equals(Convert(b)): True

-- a == b --

(a == b): True
(Convert(a) == b): False
(a == Convert(b)): False
(Convert(a) == Convert(b)): False
--------------------

string a = null;
string b = null;

-- string.Equals --

Equals(a, b): True
Equals(Convert(a), b): True
Equals(a, Convert(b)): True
Equals(Convert(a), Convert(b)): True

-- object.Equals --

Equals(a, b): True
Equals(Convert(a), b): True
Equals(a, Convert(b)): True
Equals(Convert(a), Convert(b)): True

-- a.Equals(b) --

a.Equals(b): System.NullReferenceException - Object reference not set to an instance of an object.
a.Equals(Convert(b)): System.NullReferenceException - Object reference not set to an instance of an object.
Convert(a).Equals(b): System.NullReferenceException - Object reference not set to an instance of an object.
Convert(a).Equals(Convert(b)): System.NullReferenceException - Object reference not set to an instance of an object.

-- a == b --

(a == b): True
(Convert(a) == b): True
(a == Convert(b)): True
(Convert(a) == Convert(b)): True

问题中出现的明显矛盾是由于在一种情况下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(序数)相等,调试起来可能会非常困难。

除了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

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)