使用object.ReferenceEquals
方法使用((object)obj1 == (object)obj2)
时会有额外的开销吗?
在第一种情况下,将涉及一个静态方法调用,并且在这两种情况下都会涉及到对象的某种形式的转换。
即使编译器平衡了这些方法,不平等又如何呢?
(object)obj != null
与......相比。
!object.ReferenceEquals(obj,null)
我认为在某些时候,会出现逻辑否定,无论是在!=运算符内,还是应用于ReferenceEquals方法的结果。你觉得怎么样?
还有可读性问题需要考虑。在检查相等性时,ReferenceEquals看起来更清晰,但对于不平等,可能会错过!
之前的 object.ReferenceEquals
,而 !=
第一种变化很难被忽视。
答案 0 :(得分:22)
使用object.ReferenceEquals方法是否有额外的开销
没有。该方法直接包含最小IL描述来执行引用相等性检查(对于记录:它相当于VB的Is
运算符)并且通常由JIT内联(特别是在定位x64时),因此否开销。
关于可读性:我个人认为object.ReferenceEquals
可能更具可读性(即使是否定形式),因为它明确表达了它的语义。对object
的强制转换可能会让一些程序员感到困惑。
我刚刚发现an article正在讨论这个问题。它更喜欢(object)x == y
因为IL足迹较小。它认为这可能有助于使用此比较来内联方法X
。但是(没有任何关于JIT的详细知识,但在逻辑上和直觉上)我认为这是错误的:如果JIT表现得像优化C ++编译器,它将在内联调用{{>后考虑方法1}},所以(为了内联方法ReferenceEquals
)内存占用将完全相同。
也就是说:选择一种方式而不是另一种方式对JIT没有任何影响,从而影响性能。
答案 1 :(得分: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
配置中也是一致的......
正如所指出的,重点应放在可读性/意义/揭示意图上。
答案 2 :(得分:3)
Object.ReferenceEquals的开销仅在加载参数时,在大多数情况下都会被JIT实现。之后,Object.ReferenceEquals和operator ==都归结为单个IL ceq运算符。在最坏的情况下,差异将是微不足道的。
更重要的是,Object.ReferenceEquals比(object)o1 ==(object)o2更有意图揭示。它在代码“我正在测试引用相等/身份”中清楚地说明,而不是将意图隐藏在一堆强制转换下。
答案 3 :(得分: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遭遇重击之后可能会加载或卸载哪些程序集,奇怪的非确定性事情就会被优化以及如何进行
这是一个很大的话题,但这里有几点:
到目前为止,JIT将追逐内联和优化注册深度调用深度,完全取决于当时还有什么。如果由于使用而最终在链上编译一次函数,并且在链的另一端进行不同的运行,则可以获得截然不同的结果。对于受延迟或UI驱动约束的许多应用程序而言,最糟糕的事情是非删减,而在较大的应用程序中,这可能会很快加起来。
!((object)a ==(object)b)和(object)a!=(object)b并不总是编译成相同的代码,因为它是真的certianly for!(a == b)和a!= b,即使没有任何显式运算符或Equals覆盖。一个'(对象)a!=(对象)b'更有可能触发.Net自己更加迂腐的调用运行时非常昂贵。
即使您目前没有操作符或等号覆盖,也可以提前和经常使用'(object)'或'RefEquals'保护,如果对JIT非常有帮助。 (对象)更好。如果你考虑JIT必须做什么来确定一个类型是否可以覆盖,并处理bizantine(sp)Equality规则等等,它就像一个迷你地狱,以及任何可以在以后公开的任何东西,你打算参考平等,你可以避免突然放缓或以后的代码。如果它已经是公开的而不是密封的,那么JIT就不能保证规则会或者有时间追查它们。
保护通常更为普遍的“无效”检查可能更为重要,虽然不是OP问题的一部分,因为相同的规则和问题通常适用。 '(object)a == null'和'!((object)a == null)'是'迂腐'的等价物。
答案 4 :(得分:2)
前面提到的article about == operator更好地提供了不完整的信息,至少在.NET 4.0上是这样的(它已经被写回2.0次了。)
它表示ReferenceEquals没有被优化/内联,只有在使用“AnyCPU”配置构建项目时才会出现这种情况。设置为'x86'或'x64'使(对象)obj == null和ReferenceEquals(object,null)最终成为相同的IL,其中两个方法都只是一个'ceq'IL指令。
所以答案是:两种方法生成的IL在Release / x86或x64版本上是相同的。
ReferenceEquals肯定更具可读性,至少符合我的口味。
答案 5 :(得分:0)
public static bool ReferenceEquals (Object objA, Object objB) {
return objA == objB;
}
http://referencesource.microsoft.com/#mscorlib/system/object.cs,4d607d6d56a93c7e