Magic behind ValueType.Equals

原文:http://blogs.msdn.com/xiangfan/archive/2008/09/01/magic-behind-valuetype-equals.aspx

在"Effective C#"一书中,Bill Wagner写到:“在你创建值类型的时候,一定要重写ValueType.Equals()”。他主要考虑的是性能问题,在ValueType.Equals函数的默认实现里,需要使用反射枚举它所有的成员变量,开销是比较大的。

实际上,公共语言运行时针对“简单”值类型作了特殊的优化。让我们来瞧一瞧。

这是rotor中ValueType.Equals函数的实现("clr/src/bcl/system/valuetype.cs"):

bool Equals(object obj)
{
    //Compare type

    //if there are no GC references in this object we can avoid reflection
    //and do a fast memcmp
    if (CanCompareBits(this))
    {
        return FastEqualsCheck(this, obj);
    }

    //Compare using reflection
}

这里最重要的两个函数"CanCompareBits"和"FastEqualsCheck"。它们都被标记了"[MethodImpl(MethodImplOptions.InternalCall)]"属性,说明它们的实现是在公共语言运行时的内部的。

通过浏览rotor的源代码,你可以在"clr/src/vm/comutilnative.cpp"中找到这两个函数的实现。

CanCompareBits函数的注释写到:“如果值类型不包含指针或者没有填充,将返回true”。而FastEqualsCheck函数使用"memcmp"进行快速的二进制比较。

你会说,这个优化实现很不错啊,我的值类型很简单,不用自己重写Equals函数了。但是请等一下,你有没有发现CanCompareBits的问题?

问题是注释中提到的条件并不能保证二进制的比较能够得到正确的结果。

设想一下,你有一个值类型A,它只有一个float类型的成员f。你定义了两个A的对象a和b,a.f=+0.0,b.f=-0.0。从逻辑上来讲,它们应该是相等的,但是它们的二进制表示却是不同的。因此没有优化的代码将返回true,而优化之后的版本将返回false。
类似的,如果你的值类型中包含了其它重写了Equals函数的值类型,那么上述的优化都可能导致错误的结果。

这是公共语言运行时的一个bug,但是它也告诉我们,你最好为你的值类型重写Equals函数 :-)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值