5 ways for equality check in .net .. why? and which to use?
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 inter开发者_开发问答face.
- 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)
1 - Reference equals checks if two reference type variables(classes, not structs) are referred to the same memory adress.
2 - The virtual Equals() method checks if two objects are equivalent. Let us say that you have this class:
class TestClass{
public int Property1{get;set}
public int Property2{get;set}
public override bool Equals(object obj)
{
if (obj.GetType() != typeof(TestClass))
return false;
var convertedObj = (TestClass)obj;
return (convertedObj.Property1 == this.Property1 && convertedObj.Property2 == this.Property2);
}
}
and you instantiate 2 objects from that class:
var o1 = new TestClass{property1 = 1, property2 = 2}
var o2 = new TestClass{property1 = 1, property2 = 2}
although the two objects are not the same instance of TestClass, the call to o1.Equals(o2) will return true.
3 - The static Equals method is used to handle problems when there is a null value in the check. Imagine this, for instance:
TestClass o1 = null;
var o2 = new TestClass{property1 = 1, property2 = 2}
If you try this:
o1.Equals(o2);
you wil get a NullReferenceException, because o1 points to nothing. To adress this issue, you do this:
Object.Equals(o1,o2);
This method is prepared to handle null references.
4 - The IEquatable interface is provided by .Net so you don't need to do casts inside your Equals method. If the compiler finds out that you have implemented the interface in a class for the type you are trying to check for equality, it will give that method priority over the Object.Equals(Object) override. For instance:
class TestClass : IEquatable<TestClass>
{
public int Property1 { get; set; }
public int Property2 { get; set; }
public override bool Equals(object obj)
{
if (obj.GetType() != typeof(TestClass))
return false;
var convertedObj = (TestClass)obj;
return (convertedObj.Property1 == this.Property1 && convertedObj.Property2 == this.Property2);
}
#region IEquatable<TestClass> Members
public bool Equals(TestClass other)
{
return (other.Property1 == this.Property1 && other.Property2 == this.Property2);
}
#endregion
}
now if we do this:
var o1 = new TestClass{property1 = 1, property2 = 2}
var o2 = new TestClass{property1 = 1, property2 = 2}
o1.Equals(o2);
The called method is Equals(TestClass), prior to Equals(Object).
5 - The == operator usually means the same as ReferenceEquals, it checks if two variables point to the same memory adress. The gotcha is that this operator can be overrided to perform other types of checks. In strings, for instance, it checks if two different instances are equivalent.
This is a usefull link to understand equalities in .Net better:
- CodeProject
The ReferenceEquals() method.
This is used to test if two given variables point (the symbol references) to the same object. It is literally equivalent to ((object)a) == ((object)b)
. If you override the comparison operator (==
) then ReferenceEquals
maintains a way to access the default behaviour.
However, if you are dealing with a value type (e.g. a struct) then this will always return false. This is because the comparison boxes each value type to a new object so naturally the references will not be equal.
The virtual Equals() method. (System.Object)
This is the default way to semantically compare two objects (of any type). Each class overrides this as they choose. By default it is equivalent to a CLR call (InternalEquals) that basically compares memory references.
Note, if two objects return true for Equals()
then GetHashCode()
on each of them must be equal. However, if the hash codes for two objects are value equivalent (i.e. obj1.GetHashCode() == obj2.GetHashCode()
) this does not means that Equals()
is true.
Your class should typically implement Equals
and GetHashCode
as a means to distinguish class instances, and must implement this or the ==
operator (ideally both) if it is a value type.
Note, for value types the default Equals
behaviour is that of ValueType.Equals()
which if you look in Reflector (or read the MSDN description) uses reflection to compare the members of the two value instances.
The static Equals() method.
This is equivalent to return ((objA == objB) || (((objA != null) && (objB != null)) && objA.Equals(objB)))
where each type is converted to Object
for testing. My testing shows that overloaded comparison operators are ignored, but your Equals
method will be used if the objects are not null and aren't the same reference. As such, a.Equals(b)
does not necessarily equal object.Equals(a, b)
(for the cases where ((object)a) == ((object)b)
or either a or b is null).
The Equals method from IEquatable interface.
IEquatable provides a way for you to treat comparison to instances of the same class specially. Having said that your Equals
method should be handling the behaviour the same way:
If you implement Equals, you should also override the base class implementations of Object.Equals(Object) and GetHashCode so that their behavior is consistent with that of the IEquatable.Equals method
Nevertheless you should implement IEquatable:
To handle the possibility that objects of a class will be stored in an array or a generic collection object, it is a good idea to implement IEquatable so that the object can be easily identified and manipulated.
The comparison operator ==
The comparison operator by default returns true when both of your objects are the same reference.
It is not recommended to override the comparison operator unless you are dealing with a value type (in which case it is recommended, along with the Equals
method) or an immutable reference type which you would usually compare by value (e.g. string
). Always implement !=
at the same time (in fact I get a requires a matching operator '!=' to also be defined
error if I do not).
Resources:
- Link
- Where is the implementation of InternalEquals(object objA, object objB)
- http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx
- http://msdn.microsoft.com/en-us/library/2dts52z7.aspx
- http://msdn.microsoft.com/en-us/library/ms131190.aspx
- http://msdn.microsoft.com/en-us/library/ms173147(VS.80).aspx
- http://msdn.microsoft.com/en-us/library/ms182276(VS.80).aspx
Each version of equality is slightly different.
ReferenceEquals
tests for reference equality.
virtual Equals
by default checks for reference equality for class types and value equality for struct types. It can be overridden to define equality differently, if desired; and should be overridden for value types.
static Equals
just calls virtual Equals
, but also allows for null
arguments.
IEquatable<T>.Equals
is a generic/type-safe equivalent for virtual Equals
.
operator==
is intended to be like the default virtual Equals
, meaning reference equality for class types (unless the class also overrides other operators). It should also be overridden for value types.
If you write your own collection class, use IEqualityComparer<T>
, defaulting to EqualityComparer<T>.Default
. Don't use any of the equality comparisons directly.
For primitives, stick with the == operator.
In most objects supplied in the .NET framework and any custom objects you create the .Equals() method and the == operator will only check to see if two objects refer to the same object on the heap.
The purpose of the IEquatable interface is to override the .Equals() method to change its behavior from checking for referential equality to check for value equality. The System.String type is an example of a built-in .NET object which implements this interface.
The .ReferenceEquals() method provides a way for developers who've overriden the standard .Equals() method to still be able to check two objects for referential equality.
精彩评论