С преобразованием значимого типа в объект и обратно все понятно — там появляются доп. операции на упаковку/распаковку.
А есть ли какие-то затраты при преобразовании объектов между дочерним типом и базовым типом?
Неявные преобразования проверяются компилятором и после компиляции уже не существуют, соответственно, дополнительных затрат не требуют.
Это можно проверить, прочитав IL-код после компиляции:
Код на C#:
class A{}
class B : A{}
...
B b = new B();
A a = new A();
A target;
target = b;
Console.WriteLine(target);
target = a;
Console.WriteLine(target);
Сгенерерированный IL:
IL_0001: newobj instance void B::.ctor() //new B()
IL_0006: stloc.0 //b =
IL_0007: newobj instance void A::.ctor() //new A()
IL_000c: stloc.1 //a =
IL_000d: ldloc.0
IL_000e: stloc.2
IL_000f: ldloc.2
IL_0010: call void [mscorlib]System.Console::WriteLine(object)
IL_0015: nop
IL_0016: ldloc.1
IL_0017: stloc.2
IL_0018: ldloc.2
IL_0019: call void [mscorlib]System.Console::WriteLine(object)
Как видно, в обоих случаях генерируется пара инструкций ldloc
(загрузка переменной в стек) и stloc
(выгрузка из стека в переменную). Никаких дополнительных телодвижений при преобразовании типа не выполняется.
Аналогично, если Вы вызываете метод вида: A a = someClass.generateA()
, то среда выполнения не проверяет реальный тип объекта, который вернулся из A, ожидая, что компилятор уже выполнил проверку соответствия типов.
Явные преобразования генерируют дополнительные инструкции, которые требуют определенных затрат. Приведение типа — инструкцию castclass
, преобразования через as
и is
— isinst
. Обе эти инструкции описаны в спецификации CLI.
Соответственно, код на C#:
A a = new B();
Console.WriteLine((B) a);
Console.WriteLine(a as B);
Console.WriteLine(a is B);
будет преобразован в следующий IL:
IL_0001: newobj instance void B::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: castclass B
IL_000d: call void [mscorlib]System.Console::WriteLine(object)
IL_0012: nop
IL_0013: ldloc.0
IL_0014: isinst B
IL_0019: call void [mscorlib]System.Console::WriteLine(object)
IL_001e: nop
IL_001f: ldloc.0
IL_0020: isinst B
IL_0025: ldnull
IL_0026: cgt.un
IL_0028: call void [mscorlib]System.Console::WriteLine(bool)
Если типы не сходятся, то castclass
сгенерирует исключение, что несколько более затратно. Соответственно, если вероятность этого высока, то лучше применять as
чем cast
. В случае когда типы сходятся ощутимой разницы быть не должно.
Обе инструкции применяются очень часто, работают быстро и крайне оптимизированы. Навряд ли приведение типа будет узким местом в Вашем коде, поэтому в большинстве случаев важно писать то приведение, которое имеет больше смысла и лучше читается.
Достаточно обширное обсуждение разницы as vs cast
на английском Stack Overflow: Casting vs using the 'as' keyword in the CLR
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
На скриншотах: первая строка ListView, вторая строка DataGridView
Всем привет, подскажите как вывести стринговое значение (string) в labelText (C#, Visual Studio 2017, Windows Form Application);