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