0

我在 .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)

返回空值。

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

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 上的这个 .Equals 运算符不起作用:

ValueType.Equals(object obj)
{
    ...

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

    ...
4

5 回答 5

2

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

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

编辑:规则在 MSDN上指定。

于 2009-05-13T19:38:30.487 回答
2

您正在寻找的是ValueType.Equals

在许多情况下,这只是对值进行一点一点的比较。在某些情况下,尽管它会使用反射来验证字段。

编辑

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

于 2009-05-13T19:39:10.127 回答
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

int那么和是怎么回事float?你可以和C#比较一下==,他们没有op_Equality方法?!解释是这些重载是由C# Language Specification定义的。请参阅整数比较运算符浮点比较运算符部分。

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

顺便说一句,System.Object也没有op_Equality。它也仅由 C# 定义。请参阅参考类型相等运算符一节。请注意,它System.Object有一个ReferenceEquals编译器可能选择将此重载==转换为的方法。另请注意,规范的这一部分给出了限制,因此x == y永远不会在x或上执行装箱y

C# 规范中未提及但合法使用的值类型(如DateTimeTimeSpan和)呢?答案是,这些确实有会员。Guid==op_Equality

结论:C# 语言规范定义了许多==C# 实现必须具有的重载,其中一些重载涉及intandfloatobject

备注:虚拟实例方法Equals(object)定义在所有值类型中object并被所有值类型继承。对于本身不覆盖此方法的结构,使用overridein System.ValueType。它按值进行比较。所以xy上面一样,x.Equals(y)会正常工作。Equals是一个虚拟实例方法。另一方面==是一种static“方法”,基于两个操作数的编译时类型执行重载决议(没有“虚拟分派”)。

于 2013-01-16T23:17:53.120 回答
1

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

于 2009-05-13T19:36:34.530 回答
1

对另一个问题的回答提供了转子实现。实际代码在 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;
于 2009-05-13T19:57:29.800 回答