How to Implement IEquatable with Different Equality Checks - c#

I have a MyCustomSet class with IEquatable implemented as shown below.
This works fantastic when I want to check equality for all three sets (SetA*, SetB*, and SetC*). But requirements dictate that I also need the ability to check equality for only SetA*, SetB*, or SetC*, or combination thereof (SetA* and SetB*, SetA* and SetC*, or SetB* and SetC*) and ignore checking equality for any other Set that is not required in the check.
Currently, I'm using foreach and LINQ to iterate through the Sets to perform partial equality checks, and that works, but that doesn't seem very efficient for large datasets.
Maybe the answer is looking at me straight in the face, but I don't see it because I don't have any idea how implement IEquatable that can handle different equality checks.
Would someone assist me with some suggestions or directions as to how this may be implemented? An example would be even more appreciated.
public static class HashCode
{
public const int Start = 17;
public static int Hash<T>(this int hash, T obj)
{
var h = EqualityComparer<T>.Default.GetHashCode(obj);
return unchecked((hash * 439) + h);
}
}
public class MyCustomSetModel
{
public sealed class MyCustomSet : IEquatable<MyCustomSet>
{
public string ItemName { get; set; }
public string ItemType { get; set; }
public double SetA1 { get; set; }
public double SetA2 { get; set; }
public double SetA3 { get; set; }
public double SetB1 { get; set; }
public double SetB2 { get; set; }
public double SetB3 { get; set; }
public double SetC1 { get; set; }
public double SetC2 { get; set; }
public double SetC3 { get; set; }
public bool Equals(MyCustomSet other)
{
if (ReferenceEquals(other, null))
{
return false;
}
if (ReferenceEquals(other, this))
{
return true;
}
return
(
(this.ItemName == other.ItemName) &&
(this.ItemType == other.ItemType) &&
(this.SetA1 == other.SetA1) &&
(this.SetA2 == other.SetA2) &&
(this.SetA3 == other.SetA3) &&
(this.SetB1 == other.SetB1) &&
(this.SetB2 == other.SetB2) &&
(this.SetB3 == other.SetB3) &&
(this.SetC1 == other.SetC1) &&
(this.SetC2 == other.SetC2) &&
(this.SetC3 == other.SetC3)
);
}
public override bool Equals(object obj) => Equals(obj as MyCustomSet);
public override int GetHashCode()
{
unchecked
{
return HashCode.Start
.Hash(ItemName)
.Hash(ItemType)
.Hash(SetA1)
.Hash(SetA2)
.Hash(SetA3)
.Hash(SetB1)
.Hash(SetB2)
.Hash(SetB3)
.Hash(SetC1)
.Hash(SetC2)
.Hash(SetC3);
}
}
}
}
* Update: *
Thanks to Matthew Watson who pointed me in the right direction. So I implemented a custom comparer as follows. It looks like it is working, but if anyone sees potential problems or room for better implementation, please feel free to comment.
public sealed class MyCustomSetComparer : IEqualityComparer<MyCustomSet>
{
private bool _compareSetA;
private bool _compareSetB;
private bool _compareSetC;
public MyCustomSetComparer(bool compareSetA = true, bool compareSetB = true, bool compareSetC = true)
{
_compareSetA = compareSetA;
_compareSetB = compareSetB;
_compareSetC = compareSetC;
}
public bool Equals(MyCustomSet x, MyCustomSet y)
{
if (Object.ReferenceEquals(x, y))
{
return true;
}
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
{
return false;
}
bool result =
(x.ItemName == y.ItemName) &&
(x.ItemType == y.ItemType);
if (_compareSetA)
{
result = result &&
(x.SetA1 == y.SetA1) &&
(x.SetA2 == y.SetA2) &&
(x.SetA3 == y.SetA3);
}
if (_compareSetB)
{
result = result &&
(x.SetB1 == y.SetB1) &&
(x.SetB2 == y.SetB2) &&
(x.SetB3 == y.SetB3);
}
if (_compareSetC)
{
result = result &&
(x.SetC1 == y.SetC1) &&
(x.SetC2 == y.SetC2) &&
(x.SetC3 == y.SetC3);
}
return result;
}
public int GetHashCode(MyCustomSet item)
{
if (Object.ReferenceEquals(item, null))
{
return 0;
}
int hash = HashCode.Start
.Hash(item.ItemName)
.Hash(item.ItemType);
if (_compareSetA)
{
hash = hash.Hash(item.SetA1)
.Hash(item.SetA2)
.Hash(item.SetA3);
}
if (_compareSetB)
{
hash = hash.Hash(item.SetB1)
.Hash(item.SetB2)
.Hash(item.SetB3);
}
if (_compareSetC)
{
hash = hash.Hash(item.SetC1)
.Hash(item.SetC2)
.Hash(item.SetC3);
}
unchecked
{
return hash;
}
}
}

As per Matthew Watson's comment, this is a perfect time to use IEqualityComparer<T> rather than IEquatable<T>. I would suggest only implementing IEquatable<T> when there's a single obvious, natural definition of equality for a type. When there are multiple options for equality and no single option is more reasonable than the others, implement IEqualityComparer<T> - either in several different implementation types, or in a single implementation type that is parameterized in terms of creation.

Related

c# CompareTo not behaving as expected along with SortedSets

I'm storing users table-column-configuration in a simple class:
public class ColumnUserSetting : IComparable<ColumnUserSetting>
{
public String TableWrapperName { get; set; }
public String ColumnName { get; set; }
public Boolean Enabled { get; set; }
public int Width { get; set; }
public int Position { get; set; }
}
}
these classes are stored inside a SortedSet - so, it needed to implement IComparable<>, which i implemented based on position, as the documentation says its about position comparison - nothing said they can't be the same:
public class ColumnUserSetting : IComparable<ColumnUserSetting>
{
public String TableWrapperName { get; set; }
public String ColumnName { get; set; }
public Boolean Enabled { get; set; }
public int Width { get; set; }
public int Position { get; set; }
public int CompareTo(ColumnUserSetting other)
{
if (other.Position == this.Position) return 0;
if (other.Position > this.Position) return -1;
return 1;
}
}
However, this seems to behave like "equals" in the same run. Entries having the SAME Position are overwriting each other within the set. (Even if table an column is different)
the MSDN Docu says: "Types that implement IComparable must override Equals. Types that override Equals must also override GetHashCode; otherwise, Hashtable might not work correctly."
So, I implemented these two as well, with no success:
public class ColumnUserSetting : IComparable<ColumnUserSetting>
{
public String TableWrapperName { get; set; }
public String ColumnName { get; set; }
public Boolean Enabled { get; set; }
public int Width { get; set; }
public int Position { get; set; }
public int CompareTo(ColumnUserSetting other)
{
if (other.Position == this.Position) return 0;
if (other.Position > this.Position) return -1;
return 1;
}
public override bool Equals(object obj)
{
if (!(obj is ColumnUserSetting))
return false;
ColumnUserSetting cus = (ColumnUserSetting)obj;
return (this.TableWrapperName == cus.TableWrapperName &&
this.ColumnName == cus.TableWrapperName &&
this.Enabled == cus.Enabled &&
this.Width == cus.Width &&
this.Position == cus.Position);
}
public override int GetHashCode()
{
var hashcode = 352033288;
hashcode = hashcode * -1521134295 + TableWrapperName.GetHashCode();
hashcode = hashcode * -1521134295 + ColumnName.GetHashCode();
hashcode = hashcode * -1521134295 + Enabled.GetHashCode();
hashcode = hashcode * -1521134295 + Width.GetHashCode();
hashcode = hashcode * -1521134295 + Position.GetHashCode();
return hashcode;
}
}
Only way to get the SortedSet to work as expected was to handle entries of different tables with another result from CompareTo:
public int CompareTo(ColumnUserSetting other)
{
if (this.TableWrapperName != other.TableWrapperName)
return String.Compare(this.TableWrapperName, other.TableWrapperName);
if (other.Position == this.Position) return 0;
if (other.Position > this.Position) return -1;
return 1;
}
Is this a bug or a feature?
If we inspect the reference source code for SortedSet, we can look at the implementation of AddIfNotPresent(). This returns true if an item was added, or false if the item already exists.
Near the start of the method, we have:
int order = 0;
while (current != null) {
order = comparer.Compare(item, current.Item);
if (order == 0) {
// We could have changed root node to red during the search process.
// We need to set it to black before we return.
root.IsRed = false;
return false;
}
So it is only calling the Compare() method to see if the item is the same. Thus, for your class, it only cares if Position is the same. If it is, the new item is not added.
I would say that this is a deliberate design - it's not a bug.
You will have to change your CompareTo() implementation so that it compares all the same elements as the Equals(). Just call each element's CompareTo() to do a complete ordering.

Determining if two instances are the same except for two properties

I have a class with ten properties, and am looking for objects that have the same values in these properties except for two specific properties.
I'm thinking of extending a base class which has the eight properties that I want to compare, and then extend this base class, calling the base Equals method?
What would be the least code-intensive way of determining this?
Whenever you wish to compare two instances of a custom class for value equality (that is two objects with the same value or values) rather than reference quality (that two object references refer to the same underlying object), you must take this into account in the design of the object. There is a pattern you can follow to do this. In a nutshell, it involves implementing the System.IEquatable<T> interface, which defines a method with the signature bool Equals(MyClass other). You implement this method to return true when other has the same 'value' is this object. Here is a basic example ofsimple object that has 4 properties that determine its value equality:
class MyClass : IEquatable<MyClass>
{
public int ImportantProperty1 { get; set; }
public int ImportantProperty2 { get; set; }
public int ImportantProperty3 { get; set; }
public int ImportantProperty4 { get; set; }
public int NonImportantProperty { get; set; }
public bool Equals(MyClass other)
{
return
(!Object.ReferenceEquals(this, null)) &&
(this.ImportantProperty1 == other.ImportantProperty1) &&
(this.ImportantProperty2 == other.ImportantProperty2) &&
(this.ImportantProperty3 == other.ImportantProperty3) &&
(this.ImportantProperty4 == other.ImportantProperty4);
}
}
With the above code you will be able to do the following:
MyClass a = new MyClass() { };
MyClass b = new MyClass() { };
if (a.Equals(b))
Console.WriteLine("a and b are equal");
This is the bare minimum. However, as noted in the linked article, you might want to consider the following optimizations:
Override the virtual Object.Equals(Object) method so that calls the type specific Equals method. This will allow you to compare MyClass with objects of other types:
public override bool Equals(object obj)
{
return this.Equals(obj as MyClass);
}
Add to bool Equals(MyClass) method a check to see if other references the same object as this:
public bool Equals(MyClass other)
{
if (Object.ReferenceEquals(this, other))
return true;
return
(!Object.ReferenceEquals(this, null)) &&
(this.ImportantProperty1 == other.ImportantProperty1) &&
(this.ImportantProperty2 == other.ImportantProperty2) &&
(this.ImportantProperty3 == other.ImportantProperty3) &&
(this.ImportantProperty4 == other.ImportantProperty4);
}
Override Object.GetHashCode() method so that two objects that have value equality produce the same hash code. This is the pattern I use when implement this method in this kind of scenario:
public override int GetHashCode()
{
unchecked {
int hash = 17;
hash = hash * 23 + ImportantProperty1.GetHashCode();
hash = hash * 23 + ImportantProperty2.GetHashCode();
hash = hash * 23 + ImportantProperty3.GetHashCode();
hash = hash * 23 + ImportantProperty4.GetHashCode();
return hash;
}
}
Optionally override the == and != operators. Unless these are overridden they will default reference equality. See the linked article for an example.
Here's my example in full:
namespace ValueEquality
{
class MyClass : IEquatable<MyClass>
{
public int ImportantProperty1 { get; set; }
public int ImportantProperty2 { get; set; }
public int ImportantProperty3 { get; set; }
public int ImportantProperty4 { get; set; }
public int NonImportantProperty { get; set; }
public bool Equals(MyClass other)
{
if (Object.ReferenceEquals(this, other))
return true;
return
(!Object.ReferenceEquals(this, null)) &&
(this.ImportantProperty1 == other.ImportantProperty1) &&
(this.ImportantProperty2 == other.ImportantProperty2) &&
(this.ImportantProperty3 == other.ImportantProperty3) &&
(this.ImportantProperty4 == other.ImportantProperty4);
}
public override bool Equals(object obj)
{
return this.Equals(obj as MyClass);
}
public override int GetHashCode()
{
unchecked {
int hash = 17;
hash = hash * 23 + ImportantProperty1.GetHashCode();
hash = hash * 23 + ImportantProperty2.GetHashCode();
hash = hash * 23 + ImportantProperty3.GetHashCode();
hash = hash * 23 + ImportantProperty4.GetHashCode();
return hash;
}
}
}
class Program
{
static void Main(string[] args)
{
MyClass a = new MyClass() { };
MyClass b = new MyClass() { };
if (a.Equals(b))
Console.WriteLine("a and b are equal");
}
}
}

Overloading == operator for class containing only string attributes

What would be the best (most elegant or performing) way of overloading the equality operator on a class containing only string attributes?
Example:
class MagicClass
{
public string FirstAttribute { get; set; }
public string SecondAttribute { get; set; }
public string ThirdAttribute { get; set; }
public string FourthAttribute { get; set; }
public string FifthAttribute { get; set; }
}
I know how to overload the operator itself, however, I am wondering about the following points:
Is there a way to elegantly compare such two objects (e.g. without having to write an if statement containing mutual comparisons of all the attributes
What would be a good implementation of the GetHashCode() method in such case
How about something like this, Just create array of all properties and a loop.
internal class MagicClass
{
public string FirstAttribute { get; set; }
public string SecondAttribute { get; set; }
public string ThirdAttribute { get; set; }
public string FourthAttribute { get; set; }
public string FifthAttribute { get; set; }
private string[] AllProperties//Array of all properties
{
get
{
return new[]
{
FirstAttribute,
SecondAttribute,
ThirdAttribute,
FourthAttribute,
FifthAttribute
};
}
}
protected bool Equals(MagicClass other)
{
var thisProps = this.AllProperties;
var otherProps = other.AllProperties;
return thisProps.SequenceEqual(otherProps);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((MagicClass) obj);
}
public override int GetHashCode()
{
unchecked
{
var thisProps = this.AllProperties;
int hashCode = 0;
foreach (var prop in thisProps)
{
hashCode = (hashCode * 397) ^ (prop != null ? prop.GetHashCode() : 0);
}
return hashCode;
}
}
}
Then you can call Equals method inside your operator overload. If you're lazy to create AllProperties array you can use Reflection but IMO reflection is overkill here.
Not saying this is the 'best' or the most elegant solution, but I'd have the tendency to use an array and an index initializer, using an enumeration, so I could reuse get and set logic and in this case reset a hash code for a quick first comparison.
The advantage of the enumeration is, that you don't have to recheck your compare logic when an attribute is added, and you can prevent the overhead of resorting to reflection.
class MagicClass
{
string[] Values = new string[Enum.GetValues(typeof(MagicClassValues)).Length];
public string this[MagicClassValues Value] //and/or a GetValue/SetValue construction
{
get
{
return Values[(int)Value];
}
set
{
Values[(int)Value] = value;
hash = null;
}
}
int? hash; //buffered for optimal dictionary performance and == comparisson
public override int GetHashCode()
{
if (hash == null)
unchecked
{
hash = Values.Sum(s => s.GetHashCode());
}
return hash.Value;
}
public static bool operator ==(MagicClass v1, MagicClass v2) //used == operator, in compliance to the question, but this would be better for 'Equals'
{
if(ReferenceEquals(v1,v2))return true;
if(ReferenceEquals(v1,null) || ReferenceEquals(v2,null) || v1.GetHashCode() != v2.GetHashCode())return false;
return v1.Values.SequenceEqual(v2.Values);
}
public static bool operator !=(MagicClass v1, MagicClass v2)
{
return !(v1 == v2);
}
//optional, use hard named properties as well
public string FirstAttribute { get { return this[MagicClassValues.FirstAttribute]; } set { this[MagicClassValues.FirstAttribute] = value; } }
}
public enum MagicClassValues
{
FirstAttribute,
SecondAttribute,
//etc
}

How to Check Object Content Equality not Object Reference Equality

I have an Entity Framework Entity that looks something like this:
class ListItemEtlObject
{
public int ID { get; set; }
public string ProjectName { get; set; }
public string ProjectType { get; set; }
public string ProjectCode { get; set; }
public string ProjectDescription { get; set; }
public string JobNo { get; set; }
public string JobDescription { get; set; }
public bool Include { get; set; }
}
I am pulling items from two different data sources into IEnumerable lists. How might I go about comparing the items without using a bunch of if statements to check if there are differences between the properties' values and then set the property's value if they do not match? The idea is to keep the lists synchronized. Also list A has an ID value set, list B does not. I just feel there is a better way to do this than a bunch of
if(objectA.ProjectName != objectB.ProjectName)
{
objectA.ProjectName = objectB.ProjectName;
}
If you have control of the source object then the best declarative way to support value based equality is to implement IEquatable<T>. This does unfortunately require you to enumerate out all of those checks but it's done once at the actual object definition location and doesn't need to be repeated throughout the code base.
class ListItemEtlObject : IEquatable<ListITemEtlObject>
{
...
public void Equals(ListITemEtlObject other) {
if (other == null) {
return false;
}
return
ID == other.ID &&
ProjectName == other.ProjectName &&
ProjectType == other.ProjectType &&
... ;
}
}
Additionally you could choose to overload the equality operator on the object type and allow consumers to simply use != and == on ListItemEtlObject instances and get value equality instead of reference equality.
public static bool operator==(ListItemEtlObject left, ListItemEtlObject right) {
return EqualityComparer<ListItemEtlObject>.Default.Equals(left, right);
}
public static bool operator!=(ListItemEtlObject left, ListItemEtlObject right) {
return !(left == right);
}
The easiest way would be to provide a method on your class that computes a specific hash, much like GetHashCode, and then if two instances compute the same hash, they can be said to be equivalent.
You could simplify it using reflection =)
public virtual void SetDifferences(MyBaseClass compareTo)
{
var differences = this.GetDifferentProperties(compareTo);
differences.ToList().ForEach(x =>
{
x.SetValue(this, x.GetValue(compareTo, null), null);
});
}
public virtual IEnumerable<PropertyInfo> GetDifferentProperties(MyBaseClass compareTo)
{
var signatureProperties = this.GetType().GetProperties();
return (from property in signatureProperties
let valueOfThisObject = property.GetValue(this, null)
let valueToCompareTo = property.GetValue(compareTo, null)
where valueOfThisObject != null || valueToCompareTo != null
where (valueOfThisObject == null ^ valueToCompareTo == null) || (!valueOfThisObject.Equals(valueToCompareTo))
select property);
}
And here are a couple of tests I did for you
[TestMethod]
public void CheckDifferences()
{
var f = new OverridingGetHashCode();
var g = new OverridingGetHashCode();
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
f.Include = true;
f.GetDifferentProperties(g).Should().NotBeNull().And.HaveCount(1).And.Contain(f.GetType().GetProperty("Include"));
g.Include = true;
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
g.JobDescription = "my job";
f.GetDifferentProperties(g).Should().NotBeNull().And.HaveCount(1).And.Contain(f.GetType().GetProperty("JobDescription"));
}
[TestMethod]
public void SetDifferences()
{
var f = new OverridingGetHashCode();
var g = new OverridingGetHashCode();
g.Include = true;
f.SetDifferences(g);
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
f.Include = true;
g.Include = false;
f.SetDifferences(g);
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
f.Include.Should().BeFalse();
}

How can I improve this code: Inheritance and IEquatable<>

This is an example about what I´m trying to do:
public class Foo : IEquatable<Foo>
{
public bool Equals(Foo other)
{
Type type1 = this.GetType();
Type type2 = other.GetType();
if (type1 != type2)
return false;
if (type1 == typeof(A))
{
A a = (A)this;
A b = (A)other;
return a.Equals(b);
}
else if (type1 == typeof(B))
{
B c = (B)this;
B d = (B)other;
return c.Equals(d);
}
else
{
throw new Exception("Something is wrong");
}
}
}
public class A : Foo, IEquatable<A>
{
public int Number1 { get; set; }
public int Number2 { get; set; }
public bool Equals(A other)
{
return this.Number1 == other.Number1 && this.Number2 == other.Number2;
}
}
public class B : Foo, IEquatable<B>
{
public int Number1 { get; set; }
public int Number2 { get; set; }
public int Number3 { get; set; }
public bool Equals(B other)
{
return this.Number1 == other.Number1 && this.Number2 == other.Number2 && this.Number3 == other.Number3;
}
}
But as you can see above, I'd have to use many conditionals 'if' to identify the real type. The problem is I have to use the base class. For example:
A a = new A();
Foo foo = a;
foo.Equals(another);
As a direct answer your question, you appear to implement IEquatable<Foo> by always deferring to the (concrete) sub-class's IEquatable<self> implementation. This would look something like:
(Bad code, for demonstration only)
// You need to specify what you want when this method is called on a
// vanilla Foo object. I assume here that Foo is abstract. If not, please
// specify desired behaviour.
public bool Equals(Foo other)
{
if (other == null || other.GetType() != GetType())
return false;
// You can cache this MethodInfo..
var equalsMethod = typeof(IEquatable<>).MakeGenericType(GetType())
.GetMethod("Equals");
return (bool)equalsMethod.Invoke(this, new object[] { other });
}
But it really isn't clear why you need the equality comparisons to always go "through" the base-class's IEquatable<self> implementation.
The framework already has the virtual Equals method that will result in dispatching equality-calls to the appropriate method. In addition, EqualityComparar<T>.Default (which is used by most collection-types for making equality checks) already has the smarts to choose IEquatable<self>.Equals(self) or object.Equals(object)as appropriate.
Trying to create an implementation of equality in the base-class that just forwards the request is adding no value to anything, as far as I can see.
Without further explanation on why you need the base-class IEquatable<> implementation, I recommend just implementing equality properly on each type. For example:
public class A : Foo, IEquatable<A>
{
public int Number1 { get; set; }
public int Number2 { get; set; }
public bool Equals(A other)
{
return other != null
&& Number1 == other.Number1
&& Number2 == other.Number2;
}
public override bool Equals(object obj)
{
return Equals(obj as A);
}
public override int GetHashCode()
{
return Number1 ^ Number2;
}
}
Try this piece of code:
public class Foo : IEquatable<Foo>
{
public virtual bool Equals(Foo other)
{
return true;
}
}
public class A : Foo,IEquatable<A>
{
public int Number1 { get; set; }
public int Number2 { get; set; }
public override bool Equals(Foo other)
{
if (other.GetType() == typeof(A))
{
return Equals((A)other);
}
throw new InvalidOperationException("Object is not of type A");
}
public bool Equals(A other)
{
return this.Number1 == other.Number1 && this.Number2 == other.Number2;
}
}
public class B : Foo,IEquatable<B>
{
public int Number1 { get; set; }
public int Number2 { get; set; }
public int Number3 { get; set; }
public override bool Equals(Foo other)
{
if (other.GetType() == typeof(B))
{
return Equals((B)other);
}
throw new InvalidOperationException("Object is not of type B");
}
public bool Equals(B other)
{
return this.Number1 == other.Number1 && this.Number2 == other.Number2 && this.Number3 == other.Number3;
}
}
Note : You can use Assert functionality to do typechecking.
One option is to move the Number1 and Number2 properties to the base class, and only compare the member added to the subclass in the subclasses' equality methods.
class Foo
{
// move the common properties to the base class
public int Number1 { get; set; }
public int Number2 { get; set; }
public override bool Equals(object obj)
{
Foo objfoo = obj as Foo;
return
objfoo != null
// require objects being compared to be of
// the same derived type (optionally)
&& this.GetType() == obj.GetType()
&& objfoo.Number1 == this.Number1
&& objfoo.Number2 == this.Number2;
}
public override int GetHashCode()
{
// xor the hash codes of the elements used to evaluate
// equality
return Number1.GetHashCode() ^ Number2.GetHashCode();
}
}
class A : Foo, IEquatable<A>
{
// A has no properties Foo does not. Simply implement
// IEquatable<A>
public bool Equals(A other)
{
return this.Equals(other);
}
// can optionally override Equals(object) and GetHashCode()
// to call base methods here
}
class B : Foo, IEquatable<B>
{
// Add property Number3 to B
public int Number3 { get; set; }
public bool Equals(B other)
{
// base.Equals(other) evaluates Number1 and Number2
return base.Equals(other)
&& this.Number3 == other.Number3;
}
public override int GetHashCode()
{
// include Number3 in the hashcode, since it is used
// to evaluate equality
return base.GetHashCode() ^ Number3.GetHashCode();
}
public override bool Equals(object obj)
{
return this.Equals(obj as B);
}
}
I think that derived classes should not be handled in base classes. Usually, "Foo" will know nothing about A and B.
It's still possible to make the base IEquatable implementation virtual, allowing A and B to override it and perform their specific equality checks, even if both equality checking and checked instance are available only as "Foo" or "Object".
That would treat .Equals(Foo obj) like a more specific form of Object.Equals(Object obj).

Categories