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

时间:2008-12-24 06:32:28

标签: c# generics operators equals-operator

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

  

对于预定义的值类型,   如果,则等于运算符(==)返回true   其操作数的值相等,   否则是假的。适用于参考类型   除了string之外,==如果返回true则返回true   它的两个操作数指的是相同的   宾语。对于字符串类型,==   比较字符串的值。   用户定义的值类型可能会过载   ==运算符(参见运算符)。所以可以   但是,用户定义的引用类型   默认情况下==的行为与描述相同   以上为预定义和   用户定义的引用类型。

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

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

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

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

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

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

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); } }

12 个答案:

答案 0 :(得分:274)

正如其他人所说,只有当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);
}

除此之外,这可以避免拳击/施法。

答案 1 :(得分:130)

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

类型T不一定是引用类型,因此编译器无法做出这种假设。

然而,这将编译,因为它更明确:

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

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

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

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);
    }
  }
}

<强>输出

内联: 重载==调用

通用:

按任意键继续。 。

跟进2

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

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

导致调用重载的==运算符。我想没有指定类型(作为 where ),编译器无法推断它应该使用重载运算符......虽然我认为它有足够的信息来做出决定即使没有指定类型。

答案 2 :(得分:39)

通常,EqualityComparer<T>.Default.Equals应该使用实现IEquatable<T>或具有合理Equals实现的任何内容来完成工作。

但是,如果由于某种原因==Equals的实施方式不同,那么我对generic operators的工作应该是有用的;它支持运算符版本(等等):

  • 等于(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)

答案 3 :(得分:26)

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

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

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

答案 4 :(得分:12)

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

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

这是因为如果T可以是值类型,则可能存在x == y生成错误的情况 - 在类型没有运算符==定义的情况下。同样的情况也会发生,这更明显:

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

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

答案 5 :(得分:8)

似乎没有类约束:

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

应该意识到,class运算符中的Equals约束==继承自Object.Equals,而结构的约束覆盖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);
}

答案 6 :(得分:4)

here

有一个MSDN Connect条目

Alex Turner的回复始于:

  

不幸的是,这种行为是由   设计并不容易   解决方案,以使用==与类型   可能包含值的参数   类型。

答案 7 :(得分: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});

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

答案 8 :(得分:4)

在我的情况下,我想对单位运算符进行单元测试。我需要在相等运算符下调用代码而不显式设置泛型类型。 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);
}

答案 9 :(得分:2)

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

static bool IsLessThan(T x, T y) 
{
    return ((IComparable)(x)).CompareTo(y) <= 0;
}

答案 10 :(得分:1)


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

上述方法可行,因为在用户定义的引用类型的情况下会处理== 如果是值类型,则可以覆盖==。在这种情况下,还应定义“!=”。

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

答案 11 :(得分:0)

.Equals()对我有用,而TKey是通用类型。

public virtual TOutputDto GetOne(TKey id)
{
    var entity =
        _unitOfWork.BaseRepository
            .FindByCondition(x => 
                !x.IsDelete && 
                x.Id.Equals(id))
            .SingleOrDefault();


    // ...
}