【问题标题】:.NET: Where is the default implementation for "==" op_Equality() for value types?.NET:值类型的“==” op_Equality() 的默认实现在哪里?
【发布时间】:2009-05-13 19:30:25
【问题描述】:

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

我相信 ValueType 上的这个 .Equals 运算符由于这种类型检查(从 .NET Reflector 中提取)而不起作用:

ValueType.Equals(object obj)
{
    ...

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

    ...

【问题讨论】:

    标签: .net operator-overloading


    【解决方案1】:

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

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

    编辑: 规则指定on MSDN

    【讨论】:

    • 这是在正确的轨道上。有没有办法告诉正确的演员应该是什么?例如,它如何知道将结果转换为 float 而不是 int?某处必须有优先顺序......
    • 这是正确的。如果您编写 C# (int)1 == (float)1.0;编译器将生成 (float)1.0 == (float)1.0; 的 IL 等价物;它在编译时向上转换 int,因此在运行时类型是相等的。
    【解决方案2】:

    你要找的是ValueType.Equals

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

    编辑

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

    【讨论】:

    • 我试过了,但没有按预期工作。 ValueType.Equals((int)1, (float)1);返回 false,与:(int)1 == (float)1; 相反
    • @rally25rs,你写的代码不应该编译 AFAICT。 ValueType.Equals 是一个实例方法,只需要1个参数
    • 我刚刚注意到有一个从 Object 继承的 ValueType.Equals(object, object),以及一个在 ValueType 上的 ValueType.Equals(object)。我叫错了。哎呀! :)
    【解决方案3】:

    ==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。因此它是无用的,这就是为什么在两个JustAnotherValueType 之间使用== 时不会自动选择重载==(object x, object y) 的原因。

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

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

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

    诸如DateTimeTimeSpanGuid 之类的值类型在C# 规范中未提及,但使用== 是合法的呢?答案是,这些确实拥有op_Equality 成员。

    结论:C#语言规范定义了很多==重载,C#的实现需要这些重载,其中一些重载涉及intfloatobject

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 这是非典型案例。更常见的是它会一点一点地进行比较
      【解决方案5】:

      My answer 到另一个问题提供了转子实现。实际代码在 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;
      

      【讨论】:

        猜你喜欢
        • 2021-06-07
        • 2012-01-09
        • 1970-01-01
        • 2012-10-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-27
        • 1970-01-01
        相关资源
        最近更新 更多