For more on equality operators see the Equals Operator Pattern
Microsoft guideline: Guidelines for Overloading Equals() and Operator == (C# Programming Guide)
Find out about The ReferenceEquals() method - Standard class operator == does reference comparison, so it will end up returning false if the two arguments point to different references where both refences hold the same state. To define == operator for a reference type you must be careful to define it in terms of "RefenceEqual()" and "Equals()". If you use == within the definition you will likely get an infinite recursion. This blogs explains why. Value types do not have the same problem as "==" does not do a reference comparison, it compares the value types field by field for equality.
Following example shows how a reference object behaves when the '==' and '!=' operators are overriden and when they are not. The class 'With' has the operators overriden and the class 'Sans' has not.
Microsoft guideline: Guidelines for Overloading Equals() and Operator == (C# Programming Guide)
Find out about The ReferenceEquals() method - Standard class operator == does reference comparison, so it will end up returning false if the two arguments point to different references where both refences hold the same state. To define == operator for a reference type you must be careful to define it in terms of "RefenceEqual()" and "Equals()". If you use == within the definition you will likely get an infinite recursion. This blogs explains why. Value types do not have the same problem as "==" does not do a reference comparison, it compares the value types field by field for equality.
Following example shows how a reference object behaves when the '==' and '!=' operators are overriden and when they are not. The class 'With' has the operators overriden and the class 'Sans' has not.
class TestRefernceTypeEqualityOperator
{
internal class With
{
public string Name { get; set; }
public int UniqueId { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
return false;
if (object.ReferenceEquals(this, obj))
return true;
if (!(obj is With))
return false;
With selection2 = (With)obj;
if ((this.UniqueId == selection2.UniqueId) &&
(this.Name == selection2.Name))
return true;
return base.Equals(obj);
}
public static bool operator ==(With obj1, With obj2)
{
// IF obj1 1 is null
// so obj2 must be for equality
// ELSE obj1 is not null,
// compare it with obj2 using above Equals() operator
if (ReferenceEquals(obj1, null))
return ReferenceEquals(obj2, null);
else
return obj1.Equals(obj2);
}
public static bool operator !=(With obj1, With obj2)
{
return !(obj1 == obj2);
}
public override int GetHashCode()
{
string ensemble = this.UniqueId.ToString() + this.Name;
return ensemble.GetHashCode();
}
}
internal class Sans
{
public string Name { get; set; }
public int UniqueId { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
return false;
if (object.ReferenceEquals(this, obj))
return true;
if (!(obj is Sans))
return false;
Sans selection2 = (Sans)obj;
if ((this.UniqueId == selection2.UniqueId) &&
(this.Name == selection2.Name))
return true;
return base.Equals(obj);
}
public override int GetHashCode()
{
string ensemble = this.UniqueId.ToString() + this.Name;
return ensemble.GetHashCode();
}
}
public void Test()
{
// Note that 'with' and 'with2' are different references
// with the same state
With with = new With() { Name= "Smeg", UniqueId = 2739 };
With with2 = new With() { Name = "Smeg", UniqueId = 2739 };
With with3 = new With() { Name = "Smeg", UniqueId = 2740 };
Trace.Assert(with.Equals(with2));
Trace.Assert(with == with2);
Trace.Assert(!(with != with2));
Trace.Assert(!(with == null));
Trace.Assert(!(null == with));
Trace.Assert(!with.Equals(with3));
Trace.Assert(with != with3);
Trace.Assert(!with2.Equals(with3));
Trace.Assert(with2 != with3);
// Note that 'sans' and 'sans2' are different references
// with the same state
Sans sans = new Sans() { Name = "Smeg", UniqueId = 2739 };
Sans sans2 = new Sans() { Name = "Smeg", UniqueId = 2739 };
Sans sans3 = new Sans() { Name = "Smeg", UniqueId = 2740 };
Trace.Assert(sans.Equals(sans2));
Trace.Assert(!(sans == sans2));
Trace.Assert(sans != sans2);
Trace.Assert(!(sans == null));
Trace.Assert(!(null == sans));
Trace.Assert(!sans.Equals(sans3));
Trace.Assert(sans != sans3);
Trace.Assert(!sans2.Equals(sans3));
Trace.Assert(sans2 != sans3);
}
}
The 2 highlited lines show the difference. Without '==', '!=' operator the comparison is by reference so where 'with == with2' succeeds 'sans == sans2' fails even though the different references refer to objects with exactly the same state (and even though the 'Equals()' method does return true in this case).
No comments:
Post a Comment