简体   繁体   English

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

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

According to the documentation of the == operator in MSDN ,根据MSDN==运算符的文档,

For predefined value types, the equality operator (==) returns true if the values of its operands are equal, false otherwise.对于预定义的值类型,相等运算符 (==) 如果其操作数的值相等则返回 true,否则返回 false。 For reference types other than string, == returns true if its two operands refer to the same object. For the string type, == compares the values of the strings.对于字符串以外的引用类型,如果它的两个操作数引用相同的 object,则 == 返回 true。对于字符串类型,== 比较字符串的值。 User-defined value types can overload the == operator (see operator).用户定义的值类型可以重载 == 运算符(请参阅运算符)。 So can user-defined reference types, although by default == behaves as described above for both predefined and user-defined reference types.用户定义的引用类型也可以,尽管默认情况下 == 对预定义和用户定义的引用类型的行为与上述相同。

So why does this code snippet fail to compile?那么为什么这个代码片段会编译失败呢?

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

I get the error Operator '==' cannot be applied to operands of type 'T' and 'T' .我收到错误Operator '==' cannot be applied to operands of type 'T' and 'T' I wonder why, since as far as I understand the == operator is predefined for all types?我想知道为什么,因为据我所知==运算符是为所有类型预定义的?

Edit: Thanks, everybody.编辑:谢谢大家。 I didn't notice at first that the statement was about reference types only.起初我没有注意到该声明仅是关于引用类型的。 I also thought that bit-by-bit comparison is provided for all value types, which I now know is not correct.我还认为对所有值类型都提供了逐位比较,现在我知道这是正确的。

But, in case I'm using a reference type, would the == operator use the predefined reference comparison, or would it use the overloaded version of the operator if a type defined one?但是,如果我使用的是引用类型, ==运算符会使用预定义的引用比较,还是会使用运算符的重载版本(如果类型已定义)?

Edit 2: Through trial and error, we learned that the == operator will use the predefined reference comparison when using an unrestricted generic type.编辑 2:通过反复试验,我们了解到==运算符在使用不受限制的泛型类型时将使用预定义的引用比较。 Actually, the compiler will use the best method it can find for the restricted type argument, but will look no further.实际上,编译器将使用它能为受限类型参数找到的最佳方法,但不会进一步查找。 For example, the code below will always print true , even when Test.test<B>(new B(), new B()) is called:例如,下面的代码将始终打印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); } }

As others have said, it will only work when T is constrained to be a reference type.正如其他人所说,它仅在 T 被约束为引用类型时才有效。 Without any constraints, you can compare with null, but only null - and that comparison will always be false for non-nullable value types.没有任何约束,您可以与 null 进行比较,但只能与 null 进行比较 - 对于不可为 null 的值类型,该比较将始终为 false。

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:与其调用 Equals,不如使用IComparer<T> - 如果您没有更多信息, EqualityComparer<T>.Default是一个不错的选择:

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

Aside from anything else, this avoids boxing/casting.除此之外,这避免了拳击/铸造。

"...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.类型 T 不一定是引用类型,因此编译器无法做出这种假设。

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跟进 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.我想如果没有指定类型(作为where ),编译器无法推断它应该使用重载运算符......尽管我认为即使没有指定类型,它也会有足够的信息来做出决定。

In general, EqualityComparer<T>.Default.Equals should do the job with anything that implements IEquatable<T> , or that has a sensible Equals implementation.一般来说, EqualityComparer<T>.Default.Equals应该对任何实现IEquatable<T>或具有合理Equals实现的东西都做这项工作。

If, however, == and Equals are implemented differently for some reason, then my work on generic operators should be useful;但是,如果==Equals由于某种原因实现不同,那么我在泛型运算符方面的工作应该很有用; it supports the operator versions of (among others):它支持(除其他外)运营商版本:

  • Equal(T value1, T value2)等于(T值1,T值2)
  • NotEqual(T value1, T value2) NotEqual(T value1, T value2)
  • GreaterThan(T value1, T value2)大于(T值1,T值2)
  • LessThan(T value1, T value2)小于(T值1,T值2)
  • GreaterThanOrEqual(T value1, T value2) GreaterThanOrEqual(T value1, T value2)
  • LessThanOrEqual(T value1, T value2)小于或等于(T 值 1,T 值 2)

So many answers, and not a single one explains the WHY?这么多答案,没有一个解释为什么? (which Giovanni explicitly asked)... (乔瓦尼明确要求)...

.NET generics do not act like C++ templates. .NET 泛型的行为不像 C++ 模板。 In C++ templates, overload resolution occurs after the actual template parameters are known.在 C++ 模板中,重载解析发生在知道实际模板参数之后。

In .NET generics (including C#), overload resolution occurs without knowing the actual generic parameters.在 .NET 泛型(包括 C#)中,重载解析发生时不知道实际的泛型参数。 The only information the compiler can use to choose the function to call comes from type constraints on the generic parameters.编译器可以用来选择要调用的函数的唯一信息来自泛型参数的类型约束。

The compile can't know T couldn't be a struct (value type).编译不能知道 T 不能是结构(值类型)。 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.这是因为如果 T 可以是值类型,则可能存在x == y格式错误的情况 - 在类型没有定义运算符 == 的情况下。 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.这也失败了,因为您可以传递一个没有函数 foo 的类型 T。 C# forces you to make sure all possible types always have a function foo. C# 强制您确保所有可能的类型始终具有函数 foo。 That's done by the where clause.这是由 where 子句完成的。

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

Well in my case I wanted to unit-test the equality operator.好吧,就我而言,我想对相等运算符进行单元测试。 I needed call the code under the equality operators without explicitly setting the generic type.我需要在不显式设置泛型类型的情况下调用相等运算符下的代码。 Advises for EqualityComparer were not helpful as EqualityComparer called Equals method but not the equality operator. EqualityComparer建议没有帮助,因为EqualityComparer调用了Equals方法而不是相等运算符。

Here is how I've got this working with generic types by building a LINQ .这是我通过构建LINQ使用泛型类型的方法。 It calls the right code for == and != operators:它为==!=运算符调用正确的代码:

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

There is an MSDN Connect entry for this here没有此的MSDN连接进入这里

Alex Turner's reply starts with:亚历克斯·特纳 (Alex Turner) 的回复开头是:

Unfortunately, this behavior is by design and there is not an easy solution to enable use of == with type parameters that may contain value types.不幸的是,这种行为是设计使然,并没有一个简单的解决方案来启用 == 与可能包含值类型的类型参数。

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 (eg op_Equality, op_Inequality, op_LessThan...).只需使用泛型参数获取类型并检索所需运算符的 MethodInfo(例如 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.然后使用 MethodInfo 的 Invoke 方法执行操作符,并将对象作为参数传入。

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.可能不实用,但在使用包含几个测试的通用基类时可以方便地对您的运算符进行单元测试。

I wrote the following function looking at the latest msdn.我编写了以下函数,查看最新的 msdn。 It can easily compare two objects x and y :它可以轻松比较两个对象xy

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

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

The above will work because == is taken care of in case of user-defined reference types.以上将起作用,因为在用户定义的引用类型的情况下会处理 == 。
In case of value types, == can be overridden.在值类型的情况下, == 可以被覆盖。 In which case, "!=" should also be defined.在这种情况下,还应定义“!=”。

I think that could be the reason, it disallows generic comparison using "==".我认为这可能是原因,它不允许使用“==”进行通用比较。

You can do this with C# 11 and .NET 7+:您可以使用 C# 11 和 .NET 7+ 执行此操作:

    static void Main()
    {
        Console.WriteLine(Compare(2, 2));
        Console.WriteLine(Compare(2, 3));
    }
    static bool Compare<T>(T x, T y) where T : IEqualityOperators<T, T, bool>
    {
        return x == y;
    }

(you may prefer to use where T: INumber<T> , which covers this scenario and a lot more, but it depends on your specific needs; not all equatable types are numbers) (您可能更喜欢使用where T: INumber<T> ,它涵盖了这种情况以及更多,但这取决于您的特定需求;并非所有等同类型都是数字)

The .Equals() works for me while TKey is a generic type. .Equals()对我.Equals() ,而TKey是通用类型。

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


    // ...
}

I have 2 solutions and they're very simply.我有 2 个解决方案,它们非常简单。

Solution 1: Cast the generic typed variable to object and use == operator.解决方案 1:将泛型类型变量转换为object并使用==运算符。

Example:例子:

void Foo<T>(T t1, T t2)
{
   object o1 = t1;
   object o2 = t2;
   if (o1 == o2)
   {
      // ...
      // ..
      // . 
   }
}

Solution 2: Use object.Equals(object, object) method.解决方案 2:使用object.Equals(object, object)方法。

Example:例子:

void Foo<T>(T t1, T t2)
{
   if (object.Equals(t1, t2)
   {
       // ...
       // ..
       // .
   }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM