.NET:值类型的“==”op_Equality()的默认实现在哪里?

时间:2009-05-13 19:30:25

标签: .net operator-overloading

我在.NET Reflector中徘徊,并注意到对于像“String”这样的引用类型,“==”运算符有一个显式重载:

typeof(string).GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public)

返回:“==”运算符的System.Reflection.MethodInfo。

由于它的实施,你不能做这样的事情:

if("hi" == 3)  // compiler error, plus code would throw an exception even if it ran)

然而,同样的事情适用于价值类型:

if((int)1 == (float)1.0)  // correctly returns true
if((int)1 == (float)1.2)  // correctly returns false

我正在试图弄清楚.NET内部如何处理类型转换过程,所以我在.NET Reflector中寻找op_Equality()的实现,但是“int”没有。

typeof(int).GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public)

返回null。

那么,值类型的“==”运算符的默认实现在哪里? 我希望能够通过反思来调用它:

public bool AreEqual(object x, object y)
{
    if(x.GetType().IsValueType && y.GetType().IsValueType)
        return x == y; // Incorrect, this calls the "object" equality override
    else
        ...
}

编辑#1:

我尝试了这个,但它不起作用:

(int)1 == (float)1;                          // returns true
System.ValueType.Equals( (int)1, (float)1 ); // returns false

编辑#2:

也试过这个,但没有爱情:

object x = (int)1;
object y = (float)1.0;

bool b1 = (x == y);                 // b1 = false
bool b2 = ((ValueType)x).Equals(y); // b2 = false

我相信这个。由于这种类型检查(从.NET Reflector中翻录),ValueType上的.ququals运算符不起作用:

ValueType.Equals(object obj)
{
    ...

    RuntimeType type = (RuntimeType) base.GetType();
    RuntimeType type2 = (RuntimeType) obj.GetType();
    if (type2 != type)
    {
        return false;
    }

    ...

5 个答案:

答案 0 :(得分:2)

评估(int)1 ==(float)1.0不依赖于任何特殊的==运算符,只依赖于转换规则。

编译器会将其转换为(float)((int)1)==(float)1.0

编辑: 规则指定为on MSDN

答案 1 :(得分:2)

您正在寻找的是ValueType.Equals

在许多情况下,这只会对值进行逐位比较。在某些情况下,它会使用反射来验证字段。

编辑

您对C#比较值类型以及.Net如何比较值类型感到困惑。 ValueType.Equals是.Net中的一个函数,用于比较具有相同类型的值类型对象。 C#将发出最终调用该函数的代码。但它并没有用“int”和“float”来调用它。相反,它首先将它们转换为一种类型,该类型不会丢失任何值(double)的精度,然后比较生成的double值。这就是你看到行为上的差异的原因。

答案 2 :(得分:2)

  

== op_Equality对于值类型的默认实现在哪里?

值类型没有默认==。尝试编写自己的值类型

struct JustAnotherValueType
{
  public readonly int Field;
  public JustAnotherValueType(int f) { Field = f; }
}

制作您的类型的两个值xy。尝试用x == y跟他们说。它不会编译。你可以

(object)x == (object)y

但是这将在x(让我们称之为方框1)和拳击y(方框2)上执行装箱,然后在两个方框上执行参考比较。因此它总是会返回false。因此它没用,这就是为什么在==(object x, object y)之间使用==时不会自动选择重载JustAnotherValueType的原因。

那么intfloat会发生什么?您可以将它们与C#中的==进行比较,但它们没有op_Equality方法?!解释是这些重载由 the C# Language Specification 定义。请参阅整数比较运算符浮点运算比较运算符部分。

事实上,虽然这些运算符不作为.NET结构的成员存在,但C#语言规范定义了它们,并且C#编译器必须生成一些有效的IL来模仿它们的行为。

顺便说一下,System.Object也没有op_Equality。它也只由C#定义。请参阅引用类型相等运算符部分。请注意,System.Object具有ReferenceEquals方法,编译器可能会选择将此==的重载转换为。另请注意,规范的此部分提供了限制,以便x == y永远不会对xy执行装箱。

C#规范中未提及的DateTimeTimeSpanGuid等值类型如何使用==合法使用?答案是,这些执行拥有op_Equality成员。

结论:C#语言规范定义了大量==重载,需要C#的实现,其中一些重载涉及intfloat以及{{1 }}

备注:虚拟实例方法objectEquals(object)中定义,并由所有值类型继承。对于不自行覆盖此方法的结构,使用object中的override。它按价值进行比较。因此,对于上面的System.ValueTypexy可以正常工作。 x.Equals(y)是一种虚拟实例方法。另一方面,Equals是一个==“方法”,基于两个操作数的编译时类型执行重载决策(没有“虚拟分派”)。

答案 3 :(得分:1)

我猜你正在寻找遗传给你的结构的ValueType.Equals(object obj)。它使用反射来比较所有字段。

答案 4 :(得分:1)

My answer另一个问题提供了Rotor的实现。实际代码在CLR中实现为本机代码。

具体做法是:

// Compare the contents (size - vtable - sink block index).
BOOL ret = memcmp(
    (void *) (pThisRef+1), 
    (void *) (pCompareRef+1), 
    pThisRef->GetMethodTable()->GetBaseSize() - sizeof(Object) - sizeof(int)) == 0;