首页 文章

运算符==不能应用于C#中的泛型类型吗?

提问于
浏览
287

根据MSDN== 运算符的文档,

对于预定义的值类型,如果操作数的值相等,则相等运算符(==)返回true,否则返回false . 对于除string之外的引用类型,如果其两个操作数引用同一对象,则==返回true . 对于字符串类型,==比较字符串的值 . 用户定义的值类型可以重载==运算符(请参阅运算符) . 用户定义的引用类型也是如此,尽管默认情况下==的行为与上述预定义和用户定义的引用类型相同 .

那么为什么这段代码片段无法编译呢?

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

我收到错误操作符'=='不能应用于'T'和'T'类型的操作数 . 我想知道为什么,因为据我所知, == 运算符是为所有类型预定义的?

Edit: 谢谢大家 . 起初我没有注意到该声明仅涉及引用类型 . 我还认为为所有值类型提供了逐位比较,我现在知道这是不正确的 .

但是,如果我使用引用类型, == 运算符是否会使用预定义的引用比较,或者如果类型定义了一个,它会使用运算符的重载版本吗?

Edit 2: 通过反复试验,我们了解到 == 运算符在使用不受限制的泛型类型时将使用预定义的引用比较 . 实际上,编译器将使用它可以为限制类型参数找到的最佳方法,但不会再看了 . 例如,即使调用 Test.test<B>(new B(), new B()) ,下面的代码也将始终打印 true

class A { public static bool operator==(A x, A y) { return true; } }
class B : A { public static bool operator==(B x, B y) { return false; } }
class Test { void test<T>(T a, T b) where T : A { Console.WriteLine(a == b); } }

11 回答

  • 12

    编译不能知道T不能是struct(值类型) . 所以你必须告诉它它只能是我想的参考类型:

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

    这是因为如果T可以是值类型,则可能存在 x == y 形成不良的情况 - 在类型没有运算符==定义的情况下 . 对此更为明显的是:

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

    这也失败了,因为你可以传递一个不会的类型T.有一个功能foo . C#强制您确保所有可能的类型始终具有函数foo . 这是由where子句完成的 .

  • 24

    “...默认情况下==对预定义和用户定义的引用类型的行为如上所述 . ”

    类型T不一定是引用类型,因此编译器不能进行该假设 .

    但是,这将编译,因为它更明确:

    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?"

    我原以为泛型上的==会使用重载版本,但下面的测试会另外说明 . 有趣......我很想知道为什么!如果有人知道请分享 .

    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

    内联:重载==调用

    通用:

    按任意键继续 . . .

    Follow Up 2

    我想指出将我的比较方法改为

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

    导致调用重载的==运算符 . 我想没有指定类型(作为where),编译器可以认为即使没有指定类型,它也会有足够的信息来做出决定 .

  • 267

    正如其他人所说,只有当T被约束为参考类型时才会起作用 . 没有任何约束,您可以与null进行比较,但只能为null - 对于不可为空的值类型,该比较将始终为false .

    而不是调用Equals,最好使用 IComparer<T> - 如果您没有更多信息, EqualityComparer<T>.Default 是一个不错的选择:

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

    除了其他任何东西,这避免了拳击/铸造 .

  • 37

    通常, EqualityComparer<T>.Default.Equals 应该使用任何实现 IEquatable<T> 的任务,或者具有合理的 Equals 实现 .

    但是,如果 ==Equals 由于某种原因以不同的方式实现,那么我对generic operators的工作应该是有用的;它支持(以及其他)的 operator 版本:

    • 等于(T值1,T值2)

    • NotEqual(T value1,T value2)

    • GreaterThan(T value1,T value2)

    • LessThan(T value1,T value2)

    • GreaterThanOrEqual(T value1,T value2)

    • LessThanOrEqual(T value1,T value2)

  • 1

    这么多的答案,而不是一个答案解释了为什么? (Giovanni明确要求)......

    .NET泛型不像C模板那样 . 在C模板中,在知道实际模板参数后会发生重载解析 .

    在.NET泛型(包括C#)中,在不知道实际通用参数的情况下发生重载解析 . 编译器可用于选择要调用的函数的唯一信息来自泛型参数的类型约束 .

  • 7

    似乎没有类约束:

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

    人们应该意识到 == 运算符中的 class 约束 Equals 继承自 Object.Equals ,而结构的 class 则覆盖 ValueType.Equals .

    注意:

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

    也给出了相同的编译器错误 .

    到目前为止,我不明白为什么编译器会拒绝使用值类型相等运算符比较 . 我确实知道一个事实,这是有效的:

    bool Compare<T> (T x, T y)
    {
        return x.Equals(y);
    }
    
  • 2

    这个here有一个MSDN Connect条目

    Alex Turner的回复始于:

    不幸的是,这种行为是设计上的,并且没有一个简单的解决方案可以使用==与可能包含值类型的类型参数 .

  • 4

    如果要确保调用自定义类型的运算符,可以通过反射执行此操作 . 只需使用您的泛型参数获取类型,并检索所需运算符的MethodInfo(例如op_Equality,op_Inequality,op_LessThan ...) .

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

    然后使用MethodInfo的Invoke方法执行运算符,并将对象作为参数传入 .

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

    这将调用重载的运算符,而不是由泛型参数上应用的约束定义的运算符 . 可能不实用,但在使用包含几个测试的通用基类时,对于运算符的单元测试可能会派上用场 .

  • 3

    在我的情况下,我想对单位运算符进行单元测试 . 我需要在相等运算符下调用代码而不显式设置泛型类型 . EqualityComparer 的建议没有帮助,因为 EqualityComparer 称为 Equals 方法而不是等于运算符 .

    以下是我通过构建 LINQ 来使用泛型类型的方法 . 它为 ==!= 运算符调用正确的代码:

    /// <summary>
    /// Gets the result of "a == b"
    /// </summary>
    public bool GetEqualityOperatorResult<T>(T a, T b)
    {
        // declare the parameters
        var paramA = Expression.Parameter(typeof(T), nameof(a));
        var paramB = Expression.Parameter(typeof(T), nameof(b));
        // get equality expression for the parameters
        var body = Expression.Equal(paramA, paramB);
        // compile it
        var invokeEqualityOperator = Expression.Lambda<Func<T, T, bool>>(body, paramA, paramB).Compile();
        // call it
        return invokeEqualityOperator(a, b);
    }
    
    /// <summary>
    /// Gets the result of "a =! b"
    /// </summary>
    public bool GetInequalityOperatorResult<T>(T a, T b)
    {
        // declare the parameters
        var paramA = Expression.Parameter(typeof(T), nameof(a));
        var paramB = Expression.Parameter(typeof(T), nameof(b));
        // get equality expression for the parameters
        var body = Expression.NotEqual(paramA, paramB);
        // compile it
        var invokeInequalityOperator = Expression.Lambda<Func<T, T, bool>>(body, paramA, paramB).Compile();
        // call it
        return invokeInequalityOperator(a, b);
    }
    
  • 4

    我写了以下函数来查看最新的msdn . 它可以轻松比较两个对象 xy

    static bool IsLessThan(T x, T y) 
    {
        return ((IComparable)(x)).CompareTo(y) <= 0;
    }
    
  • 124
    bool Compare(T x, T y) where T : class { return x == y; }
    

    以上内容将起作用,因为在用户定义的引用类型的情况下会处理== .
    如果是值类型,则可以覆盖== . 在这种情况下,还应定义"!=" .

    我认为这可能是原因,它不允许使用“==”进行通用比较 .

相关问题