Can't operator == be applied to generic types in C#?

前端 未结 12 650
囚心锁ツ
囚心锁ツ 2020-11-22 02:21

According to the documentation of the == operator in MSDN,

For predefined value types, the equality operator (==) returns true if th

相关标签:
12条回答
  • 2020-11-22 02:27

    I wrote the following function looking at the latest msdn. It can easily compare two objects x and y:

    static bool IsLessThan(T x, T y) 
    {
        return ((IComparable)(x)).CompareTo(y) <= 0;
    }
    
    0 讨论(0)
  • 2020-11-22 02:31

    It appears that without the class constraint:

    bool Compare<T> (T x, T y) where T: class
    {
        return x == y;
    }
    

    One should realize that while class constrained Equals in the == operator inherits from Object.Equals, while that of a struct overrides ValueType.Equals.

    Note that:

    bool Compare<T> (T x, T y) where T: struct
    {
        return x == y;
    }
    

    also gives out the same compiler error.

    As yet I do not understand why having a value type equality operator comparison is rejected by the compiler. I do know for a fact though, that this works:

    bool Compare<T> (T x, T y)
    {
        return x.Equals(y);
    }
    
    0 讨论(0)
  • 2020-11-22 02:34

    The compile can't know T couldn't be a struct (value type). So you have to tell it it can only be of reference type i think:

    bool Compare<T>(T x, T y) where T : class { return x == y; }
    

    It's because if T could be a value type, there could be cases where x == y would be ill formed - in cases when a type doesn't have an operator == defined. The same will happen for this which is more obvious:

    void CallFoo<T>(T x) { x.foo(); }
    

    That fails too, because you could pass a type T that wouldn't have a function foo. C# forces you to make sure all possible types always have a function foo. That's done by the where clause.

    0 讨论(0)
  • 2020-11-22 02:36

    "...by default == behaves as described above for both predefined and user-defined reference types."

    Type T is not necessarily a reference type, so the compiler can't make that assumption.

    However, this will compile because it is more explicit:

        bool Compare<T>(T x, T y) where T : class
        {
            return x == y;
        }
    

    Follow up to additional question, "But, in case I'm using a reference type, would the the == operator use the predefined reference comparison, or would it use the overloaded version of the operator if a type defined one?"

    I would have thought that == on the Generics would use the overloaded version, but the following test demonstrates otherwise. Interesting... I'd love to know why! If someone knows please share.

    namespace TestProject
    {
     class Program
     {
        static void Main(string[] args)
        {
            Test a = new Test();
            Test b = new Test();
    
            Console.WriteLine("Inline:");
            bool x = a == b;
            Console.WriteLine("Generic:");
            Compare<Test>(a, b);
    
        }
    
    
        static bool Compare<T>(T x, T y) where T : class
        {
            return x == y;
        }
     }
    
     class Test
     {
        public static bool operator ==(Test a, Test b)
        {
            Console.WriteLine("Overloaded == called");
            return a.Equals(b);
        }
    
        public static bool operator !=(Test a, Test b)
        {
            Console.WriteLine("Overloaded != called");
            return a.Equals(b);
        }
      }
    }
    

    Output

    Inline: Overloaded == called

    Generic:

    Press any key to continue . . .

    Follow Up 2

    I do want to point out that changing my compare method to

        static bool Compare<T>(T x, T y) where T : Test
        {
            return x == y;
        }
    

    causes the overloaded == operator to be called. I guess without specifying the type (as a where), the compiler can't infer that it should use the overloaded operator... though I'd think that it would have enough information to make that decision even without specifying the type.

    0 讨论(0)
  • 2020-11-22 02:40

    As others have said, it will only work when T is constrained to be a reference type. Without any constraints, you can compare with null, but only null - and that comparison will always be false for non-nullable value types.

    Instead of calling Equals, it's better to use an IComparer<T> - and if you have no more information, EqualityComparer<T>.Default is a good choice:

    public bool Compare<T>(T x, T y)
    {
        return EqualityComparer<T>.Default.Equals(x, y);
    }
    

    Aside from anything else, this avoids boxing/casting.

    0 讨论(0)
  • 2020-11-22 02:43

    If you want to make sure the operators of your custom type are called you can do so via reflection. Just get the type using your generic parameter and retrieve the MethodInfo for the desired operator (e.g. op_Equality, op_Inequality, op_LessThan...).

    var methodInfo = typeof (T).GetMethod("op_Equality", 
                                 BindingFlags.Static | BindingFlags.Public);    
    

    Then execute the operator using the MethodInfo's Invoke method and pass in the objects as the parameters.

    var result = (bool) methodInfo.Invoke(null, new object[] { object1, object2});
    

    This will invoke your overloaded operator and not the one defined by the constraints applied on the generic parameter. Might not be practical, but could come in handy for unit testing your operators when using a generic base class that contains a couple of tests.

    0 讨论(0)
提交回复
热议问题