Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine if two generic type values are equal?

Update* I am so sorry... my sample code contained an error which resulted in a lot of answers I didn't understand. In stead of

Console.WriteLine("3. this.Equals   " + (go1.Equals(go2)));

I meant to write

Console.WriteLine("3. this.Equals   " + (go1.Equals(sb2)));

I'm trying to figure out how I can successfully determine if two generic type values are equal to each other. Based on Mark Byers' answer on this question I would think I can just use value.Equals() where value is a generic type. My actual problem is in a LinkedList implementation, but the problem can be shown with this simpler example.

class GenericOjbect<T> {
    public T Value { get; private set; }
    public GenericOjbect(T value) {
        Value = value;
    }
    public bool Equals(T value) {
        return (Value.Equals(value));
    }
}

Now I define an instance of GenericObject<StringBuilder> containing new StringBuilder("StackOverflow"). I would expect to get true if I call Equals(new StringBuilder("StackOverflow") on this GenericObject instance, but I get false.

A sample program showing this:

using System;
using System.Text;

class Program
{
    static void Main()
    {
        var sb1 = new StringBuilder("StackOverflow");
        var sb2 = new StringBuilder("StackOverflow");

        Console.WriteLine("StringBuilder compare");
        Console.WriteLine("1. ==            " + (sb1 == sb2));
        Console.WriteLine("2. Object.Equals " + (Object.Equals(sb1, sb2)));
        Console.WriteLine("3. this.Equals   " + (sb1.Equals(sb2)));

        var go1 = new GenericOjbect<StringBuilder>(sb1);
        var go2 = new GenericOjbect<StringBuilder>(sb2);

        Console.WriteLine("\nGenericObject compare");
        Console.WriteLine("1. ==            " + (go1 == go2));
        Console.WriteLine("2. Object.Equals " + (Object.Equals(go1, sb2)));
        Console.WriteLine("3. this.Equals   " + (go1.Equals(sb2)));
        Console.WriteLine("4. Value.Equals  " + (go1.Value.Equals(sb2.Value)));
    }
}

For the three methods of comparing two StringBuilder objects, only the StringBuilder.Equals instance method (the third line) returns true. This is what I expected. But when comparing the GenericObject objects, its Equals() method (the third line) returns false. Interestingly enough, the fourth compare method does return true. I'd think the third and fourth comparison are actually doing the same thing.

I would have expected true. Because in the Equals() method of the GenericObject class, both value and Value are of type T which in this case is a StringBuilder. Based on Mark Byers' answer in this question, I would've expected the Value.Equals() method to be using the StringBuilder's Equals() method. And as I've shown, the StringBuilder's Equal() method does return true.

I've even tried

public bool Equals(T value) {
    return EqualityComparer<T>.Default.Equals(Value, value);
}

but that also returns false.

So, two questions here:

  1. Why doesn't the code return true?
  2. How could I implement the Equals method so it does return true?
like image 679
comecme Avatar asked Jan 09 '11 13:01

comecme


People also ask

How do I compare two generic types in Swift?

The Swift standard library defines a protocol called Equatable , which requires any conforming type to implement the equal to operator ( == ) and the not equal to operator ( != ) to compare any two values of that type. All of Swift's standard types automatically support the Equatable protocol.

How do you compare T types?

If you need to compare objects of type T by more or less, then the IComparable<T> interface will do. Such a comparison is necessary, for example, when sorting. If you need to compare objects of type T for equality/inequality, you can use the IEquatable<T> interface.

How do you know if a type is generic?

Use the IsGenericType property to determine whether the type is generic, and use the IsGenericTypeDefinition property to determine whether the type is a generic type definition. Get an array that contains the generic type arguments, using the GetGenericArguments method.

Is it possible to inherit from a generic type?

You can't inherit from the generic type parameter. C# generics are very different from C++ templates. Inheriting from the type parameter requires the class to have a completely different representation based on the type parameter, which is not what happens with . NET generics.


1 Answers

As suggested in Marc Gravell's answer, the problem is with StringBuilder Equals(object) implementation that is different to the one in Equals(StringBuilder).

Then, you can ignore the problem because your code will work with any other coherently-implemented classes, or you can use dynamic to fix the problem (again as suggested by Mark Gravell).

But, given that you are not using C# 4 (so no dynamic), you can try in this way:

public bool Equals(T value)
{
   // uses Reflection to check if a Type-specific `Equals` exists...
   var specificEquals = typeof(T).GetMethod("Equals", new Type[] { typeof(T) });
   if (specificEquals != null &&
       specificEquals.ReturnType == typeof(bool))
   {
       return (bool)specificEquals.Invoke(this.Value, new object[]{value});
   }
   return this.Value.Equals(value);
}
like image 168
digEmAll Avatar answered Oct 11 '22 16:10

digEmAll