Я хочу знать, какой оператор полезен с точки зрения производительности, использовать ли
Object.GetType() == typeof(Type)
или
Object is Type
Я хочу знать, какой оператор полезен с точки зрения производительности, использовать ли
Object.GetType() == typeof(Type)
или
Object is Type
Второй:
Object is Type
Протестировало это 1'000'000'000 раз string
по сравнению с 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.
Примечание. Существует сильная смысловая разница :
- Равенство
==
проверяет равенство типа: другими словами, еслиA : B
чем тест на равенство завершится неудачно дляA.GetType() == typeof(B)
тогда какA is B
будет успешным.- Если объект
null
, он выкинетSystem.NullReferenceException
. Во втором случае он вернетсяfalse
.
Это скорее логика из точки зрения компилятора: в первом варианте вы запрашиваете объект для его типа. В случае, если это не оптимизировано, вы сначала вызываете вызов функции, а затем говорите, что он должен возвращать указатель на таблицу типов.
Во втором случае вы опускаете такие вызовы: компилятор будет специализировать его, возвращая код типа. И если Type
известен заранее, он может даже разработать очень быстрый тест для него.
Обратите также внимание на то, что для некоторых тривиальных случаев Object is Type
можно оптимизировать: например, поскольку компилятор уже может получить, что Object
не может/всегда иметь тип 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 С#, похоже, не делает этого. Вероятно, ни один из доступных компиляторов С# не будет.