Arithmomaniac
Arithmomaniac

Reputation: 4804

Equating derived classes based on their base's Equals()

I have two classes which both derive from the same parent:

public class People{
    public string BetterFoot;

    public override bool Equals(object obj){
        if (obj == null || this.GetType() != obj.GetType())
            return false;
        People o = (People)obj;
        return (this.BetterFoot == o.BetterFoot);
    }

public class LeftiesOrRighties: People{
    public string BetterHand;

    public override bool Equals(object obj){
        if (obj == null || this.GetType() != obj.GetType())
            return false;
        LeftiesOrRighties o = (LeftiesOrRighties)obj;
        return (this.BetterFoot == o.BetterFoot) &&
        (this.BetterHand == o.BetterHand)
    }
}

public class Ambidextrous: People{
    public string FavoriteHand;
}

(There are GetHashCodes in there, too, but I know that they work.) I'd like to compare collections of them, based on their root Equals():

ThoseOneHanded = new List<LeftiesOrRighties>(){new LeftiesOrRighties(){BetterFoot = "L"}};
ThoseTwoHanded = new List<Ambidextrous>(){new Ambidextrous(){BetterFoot = "L"}};
//using NUnit
Assert.That ((People)ThoseOneHanded[0], Is.EqualTo((People)ThoseTwoHanded[0])));

Unfortunately, this returns false.

Why? Shouldn't the casting make them (for all intents and purposes, if not exactly) the same type, and thus use the base methods? And if not, how do I truly cast the underlying type back to People?

Upvotes: 2

Views: 6074

Answers (2)

Alexei Levenkov
Alexei Levenkov

Reputation: 100555

As Bob Vale pointed out, the cast does not change type.

The standard solution used across the .NET Framework is to use custom object implementing IEqualityComparer or its generic variant. Then your compare/find method takes 2 objects/collections and uses comparer to perform the custom comparison.

I.e. many LINQ methods take custom compare to find/filter objects like Enumerable.Distinct

public static IEnumerable<TSource> Distinct<TSource>(
    this IEnumerable<TSource> source,
    IEqualityComparer<TSource> comparer
)

Sample comparer:

class Last3BitsComparer : IEqualityComparer<int>
{
    public bool Equals(int b1, int b2)
    {
        return (b1 & 3) == (b2 & 3);
    }
    public int GetHashCode(int bx)
    {
        return bx & 3;
    }
}

Upvotes: 0

Bob Vale
Bob Vale

Reputation: 18474

Cast doesn't change the object itself, so the result of GetType will always be the same and so your this.GetType() != obj.GetType() will be true and so the function will return false.

The following logic could potentially gain the behaviour you want (and you don't need to cast to People)

public class People 
{ 
    public string BetterFoot; 

    public override bool Equals(object obj)
    { 
        var o = obj as People;
        if (o == null) return false;
        return (this.BetterFoot = o.BetterFoot); 
} 

public class LeftiesOrRighties: People 
{ 
    public string BetterHand; 

    public override bool Equals(object obj) 
    { 
        var o = obj as LeftiesOrRighties; 
        if ( o == null) return base.Equals(obj);
        return (this.BetterFoot = o.BetterFoot) && (this.BetterHand = o.BetterHand) 
    } 
} 

public class Ambidextrous: People
{ 
    public string FavoriteHand; 
} 

Upvotes: 3

Related Questions