t运算符==可以应用于c#中的泛型类型吗?

根据MSDN==操作符的文档,

对于预定义的值类型 相等运算符(==)返回true 其操作数的值相等, 否则错误。对于参考类型 除了字符串,==返回true如果 它的两个操作数指向同一个对象 对象。对于字符串类型,== 比较字符串的值。 用户定义的值类型可能重载 ==运算符(参见operator)。所以可以 用户定义的引用类型 by default ==行为如所述 以上为预定义的和 用户自定义的引用类型

那么为什么这个代码片段编译失败呢?

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); } }
133439 次浏览

编译器不能知道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(); }

这也失败了,因为你可以传递一个没有函数foo的类型T。c#强迫你确保所有可能的类型都有一个函数foo。这是由where子句完成的。


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

以上将工作,因为==在用户定义的引用类型的情况下被照顾 对于值类型,==可以被重写。在这种情况下,“!

.

.

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

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

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

输出

< p >内联: 重载==调用

泛型:

按任意键继续…

跟进2

我想指出的是,把比较方法改为

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

导致重载的==运算符被调用。我猜没有指定类型(作为在哪里),编译器不能推断它应该使用重载操作符…尽管我认为即使没有指定类型,它也有足够的信息来做出决定。

如果没有类约束:

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

正如其他人所说,只有当T被约束为引用类型时,它才会工作。在没有任何约束的情况下,您可以与null进行比较,但只能与null进行比较——对于非空值类型,这种比较将始终为假。

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

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

除此之外,这避免了装箱/类型转换。

一般来说,EqualityComparer<T>.Default.Equals应该对任何实现了IEquatable<T>或具有合理的Equals实现的东西完成这项工作。

然而,如果==Equals由于某种原因实现不同,那么我在通用的运营商上的工作应该是有用的;它支持操作符版本的(以及其他):

  • 等于(tvalue1, tvalue2)
  • notqual (T value1, T value2)
  • GreaterThan(T value1, T value2)
  • 小于(T value1, T value2)
  • GreaterThanOrEqual(tvalue1, tvalue2)
  • LessThanOrEqual(T value1, T value2)

这个在这里有一个MSDN Connect条目

Alex Turner的回答是这样开始的:

不幸的是,这种行为是由 设计并不是一件容易的事 使用==类型的解决方案 可能包含value的参数 类型。< / p >

这么多答案,却没有一个能解释为什么?(乔瓦尼明确问了这个问题)……

. net泛型不像c++模板。在c++模板中,重载解析发生在实际模板参数已知之后。

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

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

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

我写了下面的函数来查看最新的msdn。它可以很容易地比较两个对象xy:

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

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

.Equals()适合我,而TKey是一个泛型类型。

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




// ...
}

我有两个解,它们很简单。

解决方案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)
{
// ...
// ..
// .
}
}