views:

1478

answers:

8

Are they really same? Today, I ran into this problem. Here is the dump from the Immediate Window:

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

So, both s and tvi.Header contain "Category", but == returns false and Equals returns true.

s is defined as string, tvi.Header is actually a WPF TreeViewItem.Header. So, why are they returning different results? I always thought that they were interchangable in C#.

Can anybody explain why this is?

+76  A: 

Two differences:

  • 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 go bang if you call it on null, == won't

    string x = null;
    string y = null;
    
    
    if (x.Equals(y)) // Bang
    
    
    if (x == y) // Yes
    

Note that you can avoid the latter being a problem using object.Equals:

if (object.Equals(x, y)) // Fine even if x or y is null
Jon Skeet
Doesn't == also include a Object.ReferenceEquals check for a quick `true` if they are the same object?
James Curran
@James: I'd expect that for both implementations, to be honest. But I haven't checked.
Jon Skeet
`x == y` equals false because you are checking reference equality with the object class' equality operator. `(string)x == (string)y` does in fact return true, in .Net 4.0 at least.
Virtlink
@Virtlink: Exactly; it's not polymorphic. I've edited the answer to make that more obvious.
Jon Skeet
It's true that the `==` operator is not polymorphic; however, if `==` calls any virtual methods within its implementation (and many do simply by delegating to the `Equals` method) then it could also exhibit polymorphic behavior.
LBushkin
Jon Skeet will be the first user to achieve 1 million reputation, thus breaking the site. You heard it here first, folks.
MrBoJangles
@MrBoJangles: since he'd already past 2^16, The next logical barrier would be 2^31. At, say, 230 rep a day, we're looking at... 25 millennia. We've got a while, but pays to be prepared.
Michael Petrotta
+6  A: 

Times like this is why the Good Lord(*) gave us Reflector, which let's you look at the implementation.

(*) Actually, it was a guy named Lutz, but I'm sure the Good Lord approved....

James Curran
+15  A: 

C# has two "equals" concepts: Equals and ReferenceEquals. For most classes you will encounter, the == operator uses one or the other (or both), and generally only tests for ReferenceEquals when handling reference types (but the string Class is an instance where C# already knows how to test for value equality).

  • Equals compares values. (Even though two separate int variables don't exist in the same spot in memory, they can still contain the same value.)
  • ReferenceEquals compares the reference and returns whether the operands point to the same object in memory.

Example Code:

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

s1.Equals(s2); // True
s1.ReferenceEquals(s2); // False
s1 == s2 // False - testing with ReferenceEquals
s1 == sNull // False
s1.ReferenceEquals(sNull); // False
s1.Equals(sNull); // Nono!  Explode (Exception)
palswim
This is not quite true... Try compare two actual string objects with `==` that are different reference but same value, you will get `true`. Jon Skeet explains this...
Noldorin
@Noldorin: This is why I said: "The `==` operator has to choose between them sometimes, and **generally** chooses `ReferenceEquals` when handling nullable types." `string` is an example of a nullable type for which C# does not use only `ReferenceEquals`. The OP asked about `string` objects, but really needs to understand general C# objects.
palswim
+1 Indeed, and [that is what makes comparison in C#/.NET so very confusing and seemingly unspecified](http://stackoverflow.com/questions/112625/vs-object-equalsobject-in-net/3576334#3576334).
Dimitri C.
It doesn't have to choose between them, it can do something different instead. Not often a good idea, but it can.
Jon Hanna
@Jon Hanna: Good point; you *can* overload the `==` operator to do anything you want.
palswim
@palswim you wrote in your comment "string is an example of a nullable type"but nullable types are a subcategory of value types. Strings are reference types. See Section 1.3 of the spec
Conrad Frix
+11  A: 

The Header property of the TreeViewItem is statically typed to be of type object.

Therefore the == yields false. You can reproduce this with the following simple snippet:

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
0xA3
Is this a WPF thing? I just started with WPF. I never ran into this kind of problems before in WinForm apps. So, we should always use Equals instead of ==?
miliu
It's worth noting that `s1 == s2` becomes `s1.Equals(s2)` IFF both s1 and s2 are declared as strings. String equality has special meaning in C#.
Jeff M
@miliu: As you can see from my sample this is not related to WPF but generally the case in .NET.
0xA3
+1  A: 

In addition to Jon Skeet's answer, I'd like to explain why most of the time when using == you actually get the answer true on different string instances with the same value:

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

As you can see, a and b must be different string instances, but because strings are immutable, the runtime uses so called string interning to let both a and b reference the same string in memory. The == operator for objects checks reference, and since both a and b reference the same instance, the result is true. When you change either one of them, a new string instance is created, which is why string interning is possible.

By the way, Jon Skeet's answer is not complete. Indeed, x == y is false but that is only because he is comparing objects and objects compare by reference. If you'd write (string)x == (string)y, it will return true again. So strings have their ==-operator overloaded, which calls String.Equals underneath.

Virtlink
@Virtlink: I had thought that was obvious enough from the answer, but I've edited it to be more complete. However, your answer about interning is *not* correct. `a` and `b` refer to *different* instances above; string interning is only applied to compile-time constants. Your code prints True because it calls the == overload called, which is comparing the character sequences. The string that `a` ends up referring to has *not* been interned.
Jon Skeet
Wow, it's the first time I hear the term string "interning". Thanks all for your inputs.
miliu
@Jon Skeet: Why does the string that `a` ends up referring to not interned? Is it because the compile-time value of `a` at the point of the comparison can not be determined? Would the initial `"Hell"` value be interned?
Ax
@Ax: Yes - it's not a compile-time constant. "Hell" would indeed be interned. (And "Hello" would exist in the intern pool of course due to `b`; but `a` would be the result of calling `String.Concat("Hell", "o");`
Jon Skeet
+4  A: 

The apparent contradictions that appear in the question are caused because in one case the Equals function is called on a string object, and in the other case the == operator is called on the System.Object type. string and object implement equality differently from each other (value vs. reference respectively).

Beyond this fact, any type can define == and Equals differently, so in general they are not interchangeable.

Here’s an example using double (from Joseph Albahari’s note to §7.9.2 of the C# language specification):

double x = double.NaN;
Console.WriteLine (x == x);         // False
Console.WriteLine (x != x);         // True
Console.WriteLine (x.Equals(x));    // True

He goes on to say that the double.Equals(double) method was designed to work correctly with lists and dictionaries. The == operator, on the other hand, was designed to follow the IEEE 754 standard for floating point types.

In the specific case of determining string equality, the industry preference is to use neither == nor string.Equals(string) most of the time. These methods determine whether two string are the same character-for-character, which is rarely the correct behavior. It is better to use string.Equals(string, StringComparison), which allows you to specify a particular type of comparison. By using the correct comparison, you can avoid a lot of potential (very hard to diagnose) bugs.

Here’s one example:

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

Both strings in this example look the same ("Café"), so this could be very tough to debug if using a naïve (ordinal) equality.

Jeffrey L Whitledge
+1 nice use of Diacritical marks to achieve "differences" in strings
Conrad Frix
A: 

An object is defined by an OBJECT_ID, which is unique. If A and B are objects and A == B is true, then they are the very same object, they have the same data and methods, but, this is also true:

A.OBJECT_ID == B.OBJECT_ID

if A.Equals(B) is true, that means that the two objects are in the same state, but this doesn't mean that A is the very same as B.

Strings are objects.

Note that the == and Equals operators are reflexive, simetric, tranzitive, so they are equivalentic relations (to use relational algebraic terms)

What this means: If A, B and C are objects, then:

(1) A == A is always true; A.Equals(A) is always true (reflexivity)

(2) if A == B then B == A; If A.Equals(B) then B.Equals(A) (simetry)

(3) if A == B and B == C, then A == C; if A.Equals(B) and B.Equals(C) then A.Equals(C) (tranzitivity)

Also, you can note that this is also true:

(A == B) => (A.Equals(B)), but the inverse is not true.

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.

If you and a friend get a Hamburger, which is yours and his in the same time, then you must decide to split the Hamburger into two parts, because you.getHamburger() == friend.getHamburger() is true and if this happens: friend.eatHamburger(), then your Hamburger will be eaten too.

I could write other nuances about Equals and ==, but I'm getting hungry, so I have to go.

Best regards, Lajos Arpad.

Lajos Arpad
I would like to know the cause of the downvote (I still don't see why was my comment wrong). Maybe the downvoter can tell me where he thinks I was incorrect.
Lajos Arpad
Not my downvote but there's several errors in your post that could have lead to it. That A == A does not guarantee A.Equals(A) for objects they are simply two method calls and the result of those calls are not guaranteed to be identical further (2) if A == B then B == A; If A.Equals(B) then B.Equals(A) (simetry) is not true either since A and B could have two different runtime types with each of there own overloads of Equals yielding different results and lastly A == B and B == C does not lead to A == C. Take this codestring A = "foo";string B = "foo";object C = B; A == C is false
Rune FS
If you take any Object, A == A is true. If you watch whether an Object Equals itself, it will always be true. You are commenting about the == and Equals of two different Objects. (new Foo()) == (new Foo()) will never be true, but these are two different objects despite the fact that you generate them with the same syntax. A, B and C means Objects, not function calls. If you have a function call which generates Objects, the result of two different calls will be two different Objects. A == A is always true, an Object is the very same with itself. A.Equals(A) is always true, A Equals itself
Lajos Arpad
A == A means that A.OBJECT_ID == A.OBJECT_ID and A.Equals(A) which is always true (we are talking about Object's here, not function calls)
Lajos Arpad
About symmetry: I would really be eager to see an example where A == B is true and B == A is false. Note that we are talking about A and B as Objects and we are not talking about A == B true at a time, make changes to B and B == A false later, we are talking about A == B being true and B == A being false at the very same time.
Lajos Arpad
About tranzitivity: Your example unfortunately is trivially wrong: string A = "foo";string B = "foo"; Object C = B; This leads to A == B false, A.Equals(B) true, so A == B and B == C is false in the first place. I can see that you have the same problem as miliu, not understanding the difference between Equals and ==, at least he didn't write misleading examples as comments.
Lajos Arpad
+1  A: 

It is clear that tvi.header is not a String. The == is an operator that is overloaded by String class, which means it will be working only if compiler knows that both side of the operator are String.

tia