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

发布于 2024-07-10 23:13:07 字数 1327 浏览 9 评论 0原文

根据 MSDN< 中 == 运算符的文档/a>,

对于预定义值类型, 相等运算符 (==) 返回 true,如果 其操作数的值相等, 否则为假。 对于参考类型 除了字符串之外,如果满足以下条件则 == 返回 true 它的两个操作数引用相同的 目的。 对于字符串类型,== 比较字符串的值。 用户定义的值类型可以重载 == 运算符(请参阅运算符)。 所以可以 用户定义的引用类型,尽管 默认情况下 == 的行为如所描述的 以上对于预定义和 用户定义的引用类型。

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

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

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

编辑:谢谢大家。 我一开始并没有注意到该声明仅涉及引用类型。 我还认为为所有值类型提供了逐位比较,但我现在知道这是不正确的。

但是,如果我使用引用类型,== 运算符会使用预定义的引用比较,还是会使用该运算符的重载版本(如果类型已定义)?

编辑2:通过反复试验,我们了解到==运算符在使用不受限制的泛型类型时将使用预定义的引用比较。 实际上,编译器将使用它能为受限类型参数找到的最佳方法,但不会再进一步​​寻找。 例如,下面的代码将始终打印 true,即使调用 Test.test(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); } }

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

For predefined value types, the
equality operator (==) returns true if
the values of its operands are equal,
false otherwise. 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.
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'. 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. 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:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

独夜无伴 2024-07-17 23:13:07

正如其他人所说,只有当 T 被限制为引用类型时它才会起作用。 在没有任何约束的情况下,您可以与 null 进行比较,但只能与 null 进行比较 - 对于不可为 null 的值类型,该比较始终为 false。

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

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

除了除此之外,这可以避免拳击/选角。

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.

满意归宿 2024-07-17 23:13:07

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

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

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

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

跟进其他问题,“但是,如果我使用引用类型, == 运算符会使用预定义的引用比较,还是会使用如果类型定义了运算符,则该运算符的重载版本?”

我本以为泛型上的 == 会使用重载版本,但以下测试演示了其他情况。 有趣...我很想知道为什么! 如果有人知道请分享。

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

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

单挑你×的.吻 2024-07-17 23:13:07

一般来说,EqualityComparer.Default.Equals 应该使用任何实现 IEquatable 或具有合理 Equals 的内容来完成这项工作> 实施。

但是,如果由于某种原因 ==Equals 的实现方式不同,那么我在 通用运算符 应该很有用; 它支持以下运算符版本(除其他外):

  • Equal(T value1, T value2)
  • NotEqual(T value1, T value2)
  • GreaterThan(T value1, T value2)
  • LessThan(T value1, T value2)
  • GreaterThanOrEqual(T 值1, T 值2)
  • LessThanOrEqual(T 值1, T 值2)

In general, EqualityComparer<T>.Default.Equals should do the job with anything that implements IEquatable<T>, or that has a sensible Equals implementation.

If, however, == and Equals are implemented differently for some reason, then my work on generic operators should be useful; it supports the operator versions of (among others):

  • Equal(T value1, T value2)
  • 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)
悲喜皆因你 2024-07-17 23:13:07

这么多答案,却没有一个解释为什么? (Giovanni 明确要求)....

NET 泛型的行为与 C++ 模板不同。 在 C++ 模板中,重载解析发生在已知实际模板参数之后。

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

So many answers, and not a single one explains the WHY? (which Giovanni explicitly asked)...

.NET generics do not act like C++ templates. In C++ templates, overload resolution occurs after the actual template parameters are known.

In .NET generics (including C#), overload resolution occurs without knowing the actual generic parameters. The only information the compiler can use to choose the function to call comes from type constraints on the generic parameters.

一花一树开 2024-07-17 23:13:07

编译器无法知道 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 子句完成的。

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.

一百个冬季 2024-07-17 23:13:07

看来没有类约束:

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

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);
}
忱杏 2024-07-17 23:13:07

您可以使用 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;
    }

您可能更喜欢使用 where T : INumber,它涵盖了此场景以及更多内容,但这取决于您的具体需求;并非所有可等式类型都是数字)

You can do this with C# 11 and .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)

梦初启 2024-07-17 23:13:07

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

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.

Here is how I've got this working with generic types by building a 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);
}
微凉徒眸意 2024-07-17 23:13:07

这里有一个 MSDN Connect 条目

Alex Turner 的回复开始和:

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

There is an MSDN Connect entry for this here

Alex Turner's reply starts with:

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.

一抹微笑 2024-07-17 23:13:07

如果您想确保调用自定义类型的运算符,您可以通过反射来实现。 只需使用泛型参数获取类型并检索所需运算符的 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});

这将调用您的重载运算符,而不是由应用于泛型参数的约束定义的运算符。 可能不实用,但在使用包含几个测试的通用基类时,可以方便地对操作员进行单元测试。

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.

柠檬 2024-07-17 23:13:07

我根据最新的 msdn 编写了以下函数。 它可以轻松比较两个对象xy

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

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;
}
扎心 2024-07-17 23:13:07

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

上面的代码会起作用,因为在用户定义的引用类型的情况下会处理 ==。
对于值类型,可以覆盖 ==。 在这种情况下,还应该定义“!=”。

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


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 "==".

夏尔 2024-07-17 23:13:07

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

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


    // ...
}

The .Equals() works for me while TKey is a generic type.

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


    // ...
}
因为看清所以看轻 2024-07-17 23:13:07

我有两个解决方案,而且都很简单。

解决方案 1:将泛型类型变量转换为 object 并使用 == 运算符。

示例:

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

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

例子:

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

I have 2 solutions and they're very simply.

Solution 1: Cast the generic typed variable to object and use == operator.

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.

Example:

void Foo<T>(T t1, T t2)
{
   if (object.Equals(t1, t2)
   {
       // ...
       // ..
       // .
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文