How to determine if two generic type values are equal?

前端 未结 7 2204
忘掉有多难
忘掉有多难 2021-02-08 13:29

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.WriteLin         


        
相关标签:
7条回答
  • 2021-02-08 13:40

    Compare the output of typeof() first, so you make sure you are comparing the same type of objects, then write an Equals method on X class which takes another instance of X class, and compare all properties... once you find something different, return false, else keep going till you return true.

    Cheers :)

    0 讨论(0)
  • 2021-02-08 13:44

    Line 3 with the generic object is not calling your custom written method. Instead, it is calling the base Object.Equals(object). To call your custom method, you need to pass in a T not a GenericObject<T>. Something like: go1.Equals(go2.Value)

    0 讨论(0)
  • 2021-02-08 13:45

    As Eric Lippert says in answer to this question - Overload resolution is performed at compile time.

    If you take a look at StringBuilder's implementation you will notice it overloads Equals and does not override it. This is basically the root of the problem as to why StringBuilder.Equals does not work as you expected in your example.

    Take the following 2 classes as example. Overloader is analogous to StringBuilder in the example as it overloads Equals. Overrider is very similar except it it overrides Equals instead.

    public class Overloader
    {
      public string Str {get;private set;}
      public Overloader (string str) {Str = str;}
    
      public bool Equals( Overloader str )
      {
        return this.Str.Equals( str );
      }
    }
    
    public class Overrider
    {
      public string Str {get;private set;}
      public Overrider (string str) {Str = str;}
    
      public override bool Equals( object obj )
      {
        if ( obj is Overrider )
        {
          return this.Str.Equals( (obj as Overrider).Str );
        }
        return base.Equals( obj );
      }
    }
    

    I have slightly modified your GenericObject<T> class in my example:

    class GenericOjbect<T>
    {
      public T Value {get;private set;}
      public GenericOjbect( T val ) {Value = val;}
    
      public bool Equals( T val )
      {
        return Value.Equals( val );
      }
    
      public override bool Equals( object obj )
      {
        if ( obj is T )
        {
          return this.Equals( ( T )obj );
        }
        if (obj != null && obj is GenericOjbect<T> )
        {
          return this.Equals( ( obj as GenericOjbect<T> ).Value );
        }
        return base.Equals( obj );
      }
    }
    

    In this sample program you will see that Overloader (or Stringbuilder for that matter) will return false. However, Overrider returns true.

    class Program
    {
      static void Main( string[] args )
      {
        var goOverloader1 = new GenericOjbect<Overloader>( new Overloader( "StackOverflow" ) );
        var goOverloader2 = new GenericOjbect<Overloader>( new Overloader( "StackOverflow" ) );
    
        var goOverrider1 = new GenericOjbect<Overrider>( new Overrider( "StackOverflow" ) );
        var goOverrider2 = new GenericOjbect<Overrider>( new Overrider( "StackOverflow" ) );
    
        Console.WriteLine( "Overrider  : {0}", goOverloader1.Equals( goOverloader2 ) ); //False
        Console.WriteLine( "Overloader : {0}", goOverrider1.Equals( goOverrider2 ) ); //True
      }
    }
    

    Referencing Eric Lippert again - Overload resolution is performed at compile time. Meaning that the compiler basically looks at your GenericObject<T>.Equals( T val ) like this:

    public bool Equals( T val )
    {
      return Value.Equals( (Object) val );
    }
    

    To anwser your question How to determine if two generic type values are equal?. There's two things you possibly could do.

    1. If you own all the objects that will be wrapped in GenericObject<T> ensure they all at least override Equals.
    2. You could perform some reflection magic in your GenericObject<T>.Equals(T val) to manually perform late binding.
    0 讨论(0)
  • 2021-02-08 13:47

    You can either implement IEquatable<T>, or implement a comparer class that implements IEqualityComparer<T>.

    Make sure that value you check for equality is immutable and is set only at initialization of the class.

    Another consideration would be to implement IComparer<T>, when you implement this one, you don't have to worry about the hash-code, and thus, can be implemented for mutable types/fields as well.

    Once you'll properly implement IEquatable<T> in your class, your questions will be solved.

    Update: Calling return EqualityComparer<T>.Default.Equals(Value, value); would basically return same result since there is no IEqualityComparer<T> implemented...

    0 讨论(0)
  • 2021-02-08 13:52

    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);
    }
    
    0 讨论(0)
  • 2021-02-08 13:52

    Your code looks fine. The problem here is that StringBuilder has a confusing set of Equals that are contradictory. In particular, Equals(StringBuilder) disagrees with Equals(object), even when the object is a StringBuilder.

    All that EqualityComparer<T> needs is a sane Equals(object) implementation. The interface (IEquatable<T>) is optional. Unfortunately StringBuilder doesn't have this (at least, by comparison to Equals(StringBuilder), which your third test is using).

    But in general, the advice is: use EqualityComparer<T>; this supports:

    • nullable-of-T with standard "lifted" rules
    • IEquatable-of-T
    • object.Equals
    0 讨论(0)
提交回复
热议问题