Существуют ли какие-то затраты на преобразование объектов?

125
16 февраля 2021, 03:40

С преобразованием значимого типа в объект и обратно все понятно — там появляются доп. операции на упаковку/распаковку.

А есть ли какие-то затраты при преобразовании объектов между дочерним типом и базовым типом?

Answer 1

Неявные преобразования

Неявные преобразования проверяются компилятором и после компиляции уже не существуют, соответственно, дополнительных затрат не требуют.

Это можно проверить, прочитав 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 и isisinst. Обе эти инструкции описаны в спецификации 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

READ ALSO
Изменить стиль выделения заголовка столбца DataGridView

Изменить стиль выделения заголовка столбца DataGridView

На скриншотах: первая строка ListView, вторая строка DataGridView

107
Как вывести в label.Text “string” значение (Windows Form Application)

Как вывести в label.Text “string” значение (Windows Form Application)

Всем привет, подскажите как вывести стринговое значение (string) в labelText (C#, Visual Studio 2017, Windows Form Application);

120
Изменить цвет строк в DataGridView

Изменить цвет строк в DataGridView

Пытаюсь изменить цвет строки там где значение penalty = 1

97
Перевести sql запрос в LINQ

Перевести sql запрос в LINQ

Есть часть запроса SQL

94