Object.GetType() == typeof(Type) 或 Object is Type 哪个好用?
Which is good to use: Object.GetType() == typeof(Type) or Object is Type?
我想从性能的角度知道哪个语句有用
Object.GetType() == typeof(Type)
或
Object is Type
第二个:
Object is Type
用 string
测试了这 1'000'000'000 次,而 int
得到:
//Release
00:00:18.1428040 //Object.GetType() == typeof(Type)
00:00:03.9791070 //Object is Type
//Debug
00:00:21.3545510 //Object.GetType() == typeof(Type)
00:00:06.2969510 //Object is Type
//Machine specs:
//Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz
//6 GB RAM memory
//Ubuntu 14.04 (OS)
//Runtime: Mono JIT compiler version 3.2.8
//Compiler: Mono dmcs
//Notes: ran these with some background processes, but the difference in time
// is significant enough I guess.
Note: There is a strong semantic difference between the two:
- The equality
==
checks on type equality: in other words, if A : B
than the equality test will fail for A.GetType() == typeof(B)
whereas A is B
will succeed.
- If the object is
null
, it will throw a System.NullReferenceException
. In the second case, it will return
false
.
从编译器的角度来看,这是相当合乎逻辑的:在第一个变体中,您查询对象的类型。如果没有真正优化,您首先进行函数调用,该调用然后说它必须 return 指向类型的指针-table.
在第二种情况下,您省略了此类调用:编译器将通过 returning 类型代码对其进行专门化。如果预先知道Type
,它甚至可以为它制定一个非常快速的测试。
此外请注意,对于一些微不足道的情况,Object is Type
可以优化:例如,因为编译器已经可以推导出 Object
can't/is always of type Type
.
更高级
也可以分析一下CIL虚拟机源码,对于第一个变体,是这样的:
IL_0000: ldarg.0
IL_0001: callvirt instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
IL_0006: ldtoken [mscorlib]System.Int32
IL_000b: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0010: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, class [mscorlib]System.Type)
IL_0015: ret
对于第二个变体,这是:
IL_0000: ldarg.0
IL_0001: isinst [mscorlib]System.Int32
IL_0006: ldnull
IL_0007: cgt.un
IL_0009: ret
(当然可以填写其他类型)。现在 ldarg.0
和 ret
只是使用一种方法的副产品,所以可以忽略它们。
我们看到的是,在第一个变体中,显式调用 GetType
方法,然后调用 ==
运算符。函数调用通常很昂贵。在第二个变体中,它立即检查 isinst
。该代码需要更少的字节并使用更便宜的方法。虽然性能当然取决于运行时环境的实现,但我认为可以肯定地说第二个变体在性能上几乎总是胜过第一个变体。
编译器可能会专门化第一个变体,使其运行起来与第二个变体一样高效,但 Mono C# 编译器似乎无法做到这一点。可能 none 可用的 C# 编译器会。
我想从性能的角度知道哪个语句有用
Object.GetType() == typeof(Type)
或
Object is Type
第二个:
Object is Type
用 string
测试了这 1'000'000'000 次,而 int
得到:
//Release
00:00:18.1428040 //Object.GetType() == typeof(Type)
00:00:03.9791070 //Object is Type
//Debug
00:00:21.3545510 //Object.GetType() == typeof(Type)
00:00:06.2969510 //Object is Type
//Machine specs:
//Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz
//6 GB RAM memory
//Ubuntu 14.04 (OS)
//Runtime: Mono JIT compiler version 3.2.8
//Compiler: Mono dmcs
//Notes: ran these with some background processes, but the difference in time
// is significant enough I guess.
Note: There is a strong semantic difference between the two:
- The equality
==
checks on type equality: in other words, ifA : B
than the equality test will fail forA.GetType() == typeof(B)
whereasA is B
will succeed.- If the object is
null
, it will throw aSystem.NullReferenceException
. In the second case, it will returnfalse
.
从编译器的角度来看,这是相当合乎逻辑的:在第一个变体中,您查询对象的类型。如果没有真正优化,您首先进行函数调用,该调用然后说它必须 return 指向类型的指针-table.
在第二种情况下,您省略了此类调用:编译器将通过 returning 类型代码对其进行专门化。如果预先知道Type
,它甚至可以为它制定一个非常快速的测试。
此外请注意,对于一些微不足道的情况,Object is Type
可以优化:例如,因为编译器已经可以推导出 Object
can't/is always of type Type
.
更高级
也可以分析一下CIL虚拟机源码,对于第一个变体,是这样的:
IL_0000: ldarg.0
IL_0001: callvirt instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
IL_0006: ldtoken [mscorlib]System.Int32
IL_000b: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0010: call bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type, class [mscorlib]System.Type)
IL_0015: ret
对于第二个变体,这是:
IL_0000: ldarg.0
IL_0001: isinst [mscorlib]System.Int32
IL_0006: ldnull
IL_0007: cgt.un
IL_0009: ret
(当然可以填写其他类型)。现在 ldarg.0
和 ret
只是使用一种方法的副产品,所以可以忽略它们。
我们看到的是,在第一个变体中,显式调用 GetType
方法,然后调用 ==
运算符。函数调用通常很昂贵。在第二个变体中,它立即检查 isinst
。该代码需要更少的字节并使用更便宜的方法。虽然性能当然取决于运行时环境的实现,但我认为可以肯定地说第二个变体在性能上几乎总是胜过第一个变体。
编译器可能会专门化第一个变体,使其运行起来与第二个变体一样高效,但 Mono C# 编译器似乎无法做到这一点。可能 none 可用的 C# 编译器会。