locked
5 ways for equality check in .net .. why? and which to use? RRS feed

  • Question



  • Hello,

    While learning .net (by c#) i found 5 ways for checking equality between objects.
    The ReferenceEquals() method.
    The virtual Equals() method. (System.Object)
    The static Equals() method.
    The Equals method from IEquatable interface.
    The comparison operator == .

    My question is :
    Why there are so many Equals() method along with comparison operator?
    Which one of the virtual Equals() or IEquatable's Equals() sholud be used .. (say if we use our own collection classes)

    Wednesday, July 7, 2010 6:25 AM

Answers

  • Hi,

    Yes, even simple things can be sometimes very confusing!

    Value types use value equality (two variables have the same value bits) while reference types use reference equality (two variables have the same reference).

    VALUE EQUALITY

    int x = 1;
    int y = 1;
    Console.WriteLine(x == y); // prints: true

    REFERENCE EQUALITY

    object x = 1;  // boxes 1 (creates a 1st object on the heap and copies 1 to it)
    object y = 1;  // boxes 1 (creates a 2nd object on the heap and copies 1 to it)
    Console.WriteLine(x == y); // prints: false

    VIRTUAL OBJECT.EQUALS(B)

    To get the contents of the two reference types to be compared, we must use the virtual Object.Equals() method (and hope/know that the runtime type overrides the method accordingly):

    object x = 1;  // box 1 (create a 1st object on the heap and copy 1 to it)
    object y = 1;  // box 1 (create a 2nd object on the heap and copy 1 to it)
    Console.WriteLine(x.Equals(y)); // now prints: true

    This is because the virtual Object.Equals() is now called on the real runtime type of x which is not object, but int!

    STATIC OBJECT.EQUALS(A,B)

    The static method simply if the two passed variables reference the same object or are null. If A and B do not share the same reference, the static object.Equals calls the virtual A.Equals(B). If you need to check reference identity only, use: object.ReferenceEquals(A, B).

    IEQUATABLE<T>

    When you use the virtual object.Equals() on value type, you force them to be boxed. To avoid this, you can implement the IEquatable<T>-interface, which is faster (when used with the generic type constraint).

    BINDING BEHAVIOR

    System.Object.Equals() is late bound, in the sense that the real method called by System.Object.Equals() is bound only at runtime (virtual dispatch). If we for example call O1.Equals(O2), the Equals-method will be called on the run-time type of O1, no matter what a possible base classe of O1 implemented as its Equals method.

    The C# overloaded operator == by contrast is bound at compile time (non-virtual dispatch). The compiler simply binds to the type of the operands. When you write something like if(A==B) what really gets called under the hood is the static method of Type1 with a signature like this:  public static bool operator ==(Type1 x, Type1 y) {}

    Eric Lippert's blog: Double Your Dispatch, Double Your Fun
    http://blogs.msdn.com/b/ericlippert/archive/2009/04/09/double-your-dispatch-double-your-fun.aspx


    Marcel

    • Marked as answer by BhagwatGv Wednesday, July 7, 2010 11:32 AM
    Wednesday, July 7, 2010 9:32 AM

All replies

  • answer to your second question is both

    "CLR 2.0 introduced IEquatable<T> which is an interface that allows for type safe equality comparisons.  Previously, the best available method for comparing equality was the virtual Object Equals method.  The method is loosely typed since it takes an object as a parameter.  This is easy enough to deal with on the client with a simple cast to the appropriate type. IEquatable<T> is a significant improvement over this pattern because it provides a strongly typed equals method.  This protects both the caller and callee from passing incompatible object types.  Additionally it avoids the overhead of boxing for value types." reference: jaredpar's WebLog 

    for first question each Equal has it's own significance and might be due to backward compatibility also;


    Manish Sati
    Wednesday, July 7, 2010 6:42 AM
  • 1) Object's Virtual Equals method implements
    identity, not equality meaning if 2 references point to same object or not.

    2) A type can override Object's Equals method, this Equals method can no longer be called to test for identity. To fix this, Object offers a static ReferenceEquals method.

    Wednesday, July 7, 2010 7:57 AM
  • Hi,

    Yes, even simple things can be sometimes very confusing!

    Value types use value equality (two variables have the same value bits) while reference types use reference equality (two variables have the same reference).

    VALUE EQUALITY

    int x = 1;
    int y = 1;
    Console.WriteLine(x == y); // prints: true

    REFERENCE EQUALITY

    object x = 1;  // boxes 1 (creates a 1st object on the heap and copies 1 to it)
    object y = 1;  // boxes 1 (creates a 2nd object on the heap and copies 1 to it)
    Console.WriteLine(x == y); // prints: false

    VIRTUAL OBJECT.EQUALS(B)

    To get the contents of the two reference types to be compared, we must use the virtual Object.Equals() method (and hope/know that the runtime type overrides the method accordingly):

    object x = 1;  // box 1 (create a 1st object on the heap and copy 1 to it)
    object y = 1;  // box 1 (create a 2nd object on the heap and copy 1 to it)
    Console.WriteLine(x.Equals(y)); // now prints: true

    This is because the virtual Object.Equals() is now called on the real runtime type of x which is not object, but int!

    STATIC OBJECT.EQUALS(A,B)

    The static method simply if the two passed variables reference the same object or are null. If A and B do not share the same reference, the static object.Equals calls the virtual A.Equals(B). If you need to check reference identity only, use: object.ReferenceEquals(A, B).

    IEQUATABLE<T>

    When you use the virtual object.Equals() on value type, you force them to be boxed. To avoid this, you can implement the IEquatable<T>-interface, which is faster (when used with the generic type constraint).

    BINDING BEHAVIOR

    System.Object.Equals() is late bound, in the sense that the real method called by System.Object.Equals() is bound only at runtime (virtual dispatch). If we for example call O1.Equals(O2), the Equals-method will be called on the run-time type of O1, no matter what a possible base classe of O1 implemented as its Equals method.

    The C# overloaded operator == by contrast is bound at compile time (non-virtual dispatch). The compiler simply binds to the type of the operands. When you write something like if(A==B) what really gets called under the hood is the static method of Type1 with a signature like this:  public static bool operator ==(Type1 x, Type1 y) {}

    Eric Lippert's blog: Double Your Dispatch, Double Your Fun
    http://blogs.msdn.com/b/ericlippert/archive/2009/04/09/double-your-dispatch-double-your-fun.aspx


    Marcel

    • Marked as answer by BhagwatGv Wednesday, July 7, 2010 11:32 AM
    Wednesday, July 7, 2010 9:32 AM