19

object.ReferenceEquals使用方法与使用相比是否有额外的开销((object)obj1 == (object)obj2)

在第一种情况下,将涉及静态方法调用,并且在这两种情况下都将涉及到对象的某种形式的强制转换。

即使编译器平衡了这些方法,不等式又如何呢?

(object)obj != null

相比于...

!object.ReferenceEquals(obj,null)

我想在某些时候,会发生逻辑否定,或者在 != 运算符中,或者应用于 ReferenceEquals 方法的结果。你怎么看?

还有可读性问题需要考虑。ReferenceEquals 在检查相等性时似乎更清楚,但对于不等式,人们可能会错过!前面的object.ReferenceEquals,而!=第一个变体中的 the 很难忽视。

4

6 回答 6

22

使用 object.ReferenceEquals 方法是否有额外的开销

不。该方法直接包含执行引用相等检查的最小 IL 描述(记录:它等效于 VB 的Is运算符)并且通常由 JIT 内联(尤其是针对 x64 时),因此没有开销。

关于可读性:我个人认为这object.ReferenceEquals可能更具可读性(即使是否定形式),因为它明确地表达了它的语义。转换为object可能会让一些程序员感到困惑。

我刚刚找到一篇讨论这个的文章。它更喜欢(object)x == y,因为 IL 占用空间更小。它认为这可能有助于X使用这种比较的方法内联。但是(没有任何关于 JIT 的详细知识,但在逻辑上和直观上)我认为这是错误的:如果 JIT 的行为类似于优化 C++ 编译器,它将在内联对 的调用ReferenceEquals考虑该方法,因此(为了内联方法X) 内存占用量将完全相同。

也就是说:选择一种方式而不是另一种方式对 JIT 并因此对性能没有任何影响。

于 2009-04-09T19:21:01.037 回答
5

与这里的答案相反,我发现(object) ==object.ReferenceEquals. 至于有多快,可以忽略不计!

试验台:

我知道您需要参考相等检查,但我也包括静态object.Equals(,)方法,以防它未被覆盖的类。

平台:x86;配置:发布版本

class Person {
}

public static void Benchmark(Action method, int iterations = 10000)
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    for (int i = 0; i < iterations; i++)
        method();

    sw.Stop();
    MsgBox.ShowDialog(sw.Elapsed.TotalMilliseconds.ToString());
}

测试:

Person p1 = new Person();
Person p2 = new Person();
bool b;
Benchmark(() =>
{
    b = (object)p1 == (object)p2; //960 ~ 1000ms
    b = object.ReferenceEquals(p1, p2); //~ 1250ms
    b = object.Equals(p1, p2); //2100ms
    b = EqualityComparer<Person>.Default.Equals(p1, p2); //~4000ms

}, 100000000);

Person p1 = new Person();
Person p2 = null;
bool b;
Benchmark(() =>
{
    b = (object)p1 == (object)p2; //990 ~ 1000ms
    b = object.ReferenceEquals(p1, p2); // 1230 ~ 1260ms
    b = object.Equals(p1, p2); //1250 ~ 1300ms
    b = EqualityComparer<Person>.Default.Equals(p1, p2); //~3100ms

}, 100000000);

Person p1 = null;
Person p2 = null;
bool b;
Benchmark(() =>
{
    b = (object)p1 == (object)p2; //960 ~ 1000ms
    b = object.ReferenceEquals(p1, p2); //1260 ~ 1270ms
    b = object.Equals(p1, p2); //1180 ~ 1220ms
    b = EqualityComparer<Person>.Default.Equals(p1, p2); //~3100ms

}, 100000000);

Person p1 = new Person();
Person p2 = p1;
bool b;
Benchmark(() =>
{
    b = (object)p1 == (object)p2; //960 ~ 1000ms
    b = object.ReferenceEquals(p1, p2); //1260 ~ 1280ms
    b = object.Equals(p1, p2); //1150 ~ 1200ms
    b = EqualityComparer<Person>.Default.Equals(p1, p2); //3700 ~ 3800ms

}, 100000000);

object.Equals(,)内部调用ReferenceEquals,如果它们不相等,它将调用Equals类的重写虚拟方法,因此您会看到速度差异的通知。

结果在Debug配置上也是一致的......

正如所指出的,重点应该放在可读性/意义/揭示意图上。

于 2012-12-16T08:28:22.943 回答
3

Object.ReferenceEquals 的开销仅在于加载参数,在大多数情况下会被 JIT 删除。之后, Object.ReferenceEquals 和 operator== 都归结为一个 IL ceq 运算符。在最坏的情况下,差异将是微不足道的。

更重要的是,Object.ReferenceEquals 比 (object)o1 == (object)o2 更能揭示意图。它在代码中清楚地说明了“我正在测试引用相等/身份”,而不是将意图隐藏在一堆强制转换下。

于 2009-04-09T19:23:28.563 回答
3

加上我的两分钱,在性能关键代码的许多迟到之后,在非常大的代码库上,有时疯狂的深度调用深度。

在现实世界中的“微基准测试”之外,JIT 有更多的问题和顾虑,既没有 C++ WPO 编译时的奢侈,也没有 C# 编译器更直接的翻译,但所有问题都没有在 C# 编译器完成后必须拥有所有上下文。

“迂腐”的形式:

if ((object)a == (object)b) { }     // ref equals

if (!((object)a == (object)b)) { }  // ref not equals

如果你真的有权衡和测量诚实的性能问题,或者需要为一些非常普遍的大型课程减轻 JIT 的压力,这可以提供很多帮助。NullOrEmpty vs '(object)str == null || 也是如此 str.Length == 0' 。

没有 WPO 的奢侈,并且在许多情况下不知道哪些程序集在 JITing 受到重击后可能会被加载或卸载,奇怪的非确定性事情会发生在优化的内容和方式方面。

这是一个很大的话题,但这里有几点:

  1. 到目前为止,JIT 将追逐内联并注册优化向下调用深度,并且完全取决于当时发生的其他事情。如果您最终由于使用而在链上编译了一次函数,而在更远的链上编译了一次不同的运行,您将获得截然不同的结果。对于许多受延迟或 UI 驱动的应用程序而言,最糟糕的事情是非确定性,而在较大的应用程序中,这可能会迅速增加。

  2. !((object)a == (object)b) 和 (object)a != (object)b 并不总是编译成相同的代码,对于 !(a == b) 和 a != b,即使没有任何明确的运算符或 Equals 覆盖。'(object)a != (object)b' 更有可能触发 .Net 自己更迂腐的调用进入运行时,这是非常昂贵的。

  3. 如果对 JIT 非常有帮助,请尽早并经常使用 '(object)' 或 'RefEquals' 进行保护,即使您当前没有 operator 或 Equals 覆盖。(对象)甚至更好。如果您考虑 JIT 必须做些什么来确定一个类型是否可以覆盖,并处理 bizantine (sp) Equality 规则等等,它就像一个迷你地狱,以及任何可以稍后公开的东西,你打算参考平等,您可以避免以后突然减速或不稳定的代码。如果它已经是公开的并且没有被密封,JIT 就无法确定规则将会或有时间追捕它们。

  4. 保护通常更普遍的“空”检查可能更重要,尽管不是 OP 问题的一部分,因为相同的规则和问题通常适用。'(object)a == null' 和 '!((object)a == null)' 是 'pedantic' 等价物。

于 2012-12-26T21:22:35.653 回答
2

前面提到的关于 == 运算符更好的文章提供了不完整的信息,至少在 .NET 4.0 上是这样(好吧,它是在 2.0 时代写回的)。

它指出 ReferenceEquals 没有被优化/内联,只有当您使用“AnyCPU”配置构建项目时才会如此。设置为 'x86' 或 'x64' 使得 (object)obj == null 和 ReferenceEquals(object, null) 最终成为相同的 IL,这两种方法只是一个 'ceq' IL 指令。

所以答案是:两种方法生成的 IL 在 Release / x86 或 x64 版本上是相同的。

ReferenceEquals 绝对更具可读性,至少在我看来。

于 2011-02-13T22:04:30.213 回答
0
public static bool ReferenceEquals (Object objA, Object objB) {
        return objA == objB;
    }

http://referencesource.microsoft.com/#mscorlib/system/object.cs,4d607d6d56a93c7e

于 2015-04-01T21:41:24.703 回答