Что полезно использовать: Object.GetType() == typeof (Type) или Object is Type?

Я хочу знать, какой оператор полезен с точки зрения производительности, использовать ли

Object.GetType() == typeof(Type)

или

Object is Type

Ответ 1

Второй:

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 С#, похоже, не делает этого. Вероятно, ни один из доступных компиляторов С# не будет.