B3ret
B3ret

Reputation: 618

Why is this not working? (Generic Equals Helper)

SOLVED! This works, I need to tell the compiler that T implements IEquatable of course...

public static bool secureEquals<T>(T obj1, object obj2)
    where T: class, IEquatable<T>
{...

public static bool secureEquals<T>(T obj1, T obj2)
    where T: class, IEquatable<T>
{....

Question:

I tried to put repeated functionality of IEquatable implementations and Equals overrides into a separate static class like so:

public static class EqualsHelper
{
    public static bool secureEquals<T>(T obj1, object obj2)
       where T : class
    {
        if (obj2 is T)
        {
            return secureEquals(obj1, obj2 as T);
        }
        else
        {
            return false;
        }
    }

    public static bool secureEquals<T>(T obj1, T obj2)
    {
        if (obj1 == null)
        {
            if (obj2 != null)
                return false;
        }
        else
        {
            if (!obj1.Equals(obj2)) //this calls Dummy.Equals(object other)!
                return false;
        }

        return true;
    }


    public static bool secureEquals(double[] obj1, double[] obj2)
    {
        if (obj1.Length != obj2.Length)
            return false;

        for (int i = 0; i < obj1.Length; ++i)
        {
            if (obj1[i] != obj2[i])//ok for doubles if they are generated in the same way? I guess so!
                return false;
        }

        return true;
    }

public class Dummy : IEquatable<Dummy>
{
    public Dummy(string member)
    {
        _member = member;
    }

    private string _member;


    public virtual bool Equals(Dummy other)
    {
        return this._member == other._member;
    }

    public override bool Equals(object other)
    {
        return EqualsHelper.secureEquals(this, other);
    }

}

    static void Main(string[] args)
    {
        Dummy d1 = new Dummy("Hugo");
        Dummy d2 = new Dummy("Hugo");

        object d2obj = (object)d2;

        bool ret = d1.Equals(d2obj);
    }

The idea was: d1.Equals(d2obj) calls Dummy.Equals(object) calls EqualsHelper.secureEquals(T, obj) calls EqualsHelper.secureEquals(T, T) calls Dummy.Equals(Dummy).

The last call however calls Dummy.Equals(object), even though everything is typed to T there.

What am I missing?

PS: I know that replacing the call with:

            if (!((IEquatable<T>)obj1).Equals(obj2)) //this calls Dummy.Equals(object other)!

does the trick, but why is it not working otherwise?

Upvotes: 3

Views: 411

Answers (3)

Tomislav Markovski
Tomislav Markovski

Reputation: 12346

Because in your secureEquals you have no constraints, and compiler always assumes Object.Equals exist. Add interface constraint for your T.

Upvotes: 0

Marc Gravell
Marc Gravell

Reputation: 1062855

Why: because method invoke here is static typed, and the only available Equals involving a T with no generic constraint is object.Equals(object). The exact same IL has to be able to handle every T - C# generics are not like C++ templates; no per-T overload resolution occurs.

As an aside: you might also want to look at EqualityComparer<T>.Default.Equals(obj1,obj2), which will handle IEquatable<T>, Nullable<T>, etc automatically:

public static bool secureEquals<T>(T obj1, object obj2) where T : class
{
    return EqualityComparer<T>.Default.Equals(obj1, obj2 as T);
}

Upvotes: 5

Jon Skeet
Jon Skeet

Reputation: 1500635

The Equals overload within EqualsHelper.secureEquals is resolved when EqualsHelper is compiled - and that code doesn't know whether T implements IComparable<T> or not, so all that's left is Equals(Object). You can add a constraint to T which would make it use the right overload:

public static bool SecureEquals<T>(T obj1, T obj2) where T : IEquatable<T>

Of course, this would limit the classes with which you could use it.

(As an aside, note that I've renamed secureEquals to SecureEquals to comply with .NET naming conventions. I'd also not use the word "secure" here at all personally - there's nothing security-sensitive here.)

Upvotes: 4

Related Questions