Динамический тип C# приводит к разрешению Console.WriteLine с отражением в IL.

Я экспериментировал с некоторыми операторами C# в LINQPad, чтобы понять, какой код промежуточного языка генерируется.

Сначала я попробовал следующий код:

var Container = new {Name = "James"};
Console.WriteLine(Container.Name);

И увидел следующие шесть строк IL:

IL_0001:  ldstr       "James"
IL_0006:  newobj      <>f__AnonymousType0<System.String>..ctor
IL_000B:  stloc.0     
IL_000C:  ldloc.0     
IL_000D:  callvirt    <>f__AnonymousType0<System.String>.get_Name
IL_0012:  call        System.Console.WriteLine

Что, в целом, то, что я ожидаю, и является довольно хорошей демонстрацией того, как анонимные типы доступны только для чтения/неизменяемы, учитывая отсутствие свойства set_Name.

Затем я попробовал утверждения:

dynamic Container = new System.Dynamic.ExpandoObject();
Container.Name = "James";
Console.WriteLine(Container.Name);

Это приводит к излучению огромного количества IL. Я не буду вставлять его сюда, но вы можете найти его в этой вставке.

Я понимаю, что управление динамическим типом и ExpandoObject сопряжено с большими накладными расходами, но я не понимаю, почему вызов System.Console.WriteLine в данном случае выполняется посредством внутреннего отражения.

IL_0072:  ldstr       "WriteLine"
....
IL_00BF:  ldtoken     System.Console

В первом сегменте кода, после извлечения и сохранения свойства, это был однострочный оператор IL, который вызывал System.Console.WriteLine.

Так зачем же все это нужно для вызова с типом dynamic?


person James Wiseman    schedule 20.09.2012    source источник


Ответы (2)


Поскольку переменная dynamic, во время компиляции невозможно узнать, какую перегрузку WriteLine следует вызывать. Только во время выполнения мы узнаем фактический тип объекта dynamic. Из-за того, как работает dynamic, важно, чтобы он не просто рассматривался как object во время компиляции; часть мощности заключается в том, что она определяет правильную перегрузку во время работы.

Если вы приведете объект к чему-то другому, кроме динамического (т.е. string после вызова ToString или просто вернетесь к ExpandoObject), а затем передадите его WriteLine, то вы должны увидеть, что вызов отражения исчезнет и увидите, как он статически определяет во время компиляции правильную перегрузку из WriteLine.

person Servy    schedule 20.09.2012
comment
На самом деле, ваш трюк с ToString() не сработает (если только вы не добавите явное приведение к string). - person svick; 21.09.2012

Происходит то, что компилятор создает ваш код таким образом, что его можно "поздно связать". Позднее связывание означает, что вместо разрешения ваших объектов во время компиляции, как в случае с традиционными типами данных и объектами, объект разрешается во время выполнения, в то время как ваша сборка фактически находится в памяти и выполняется.

Если бы вы посмотрели на свой код в Reflector или dotPeek, вы бы увидели, что ваши динамические объекты были украшены атрибутом [Dynamic]. Пока ваша программа работает в памяти, когда дело доходит до объекта, который был украшен этим атрибутом, вызов этого объекта передается через динамический Container (или как там называется ваш объект). Этот Container инициализируется с помощью Binder, ответственного за привязку во время выполнения. Это то, что делают все вызываемые Microsoft.CSharp.RuntimeBinder. Этот RuntimeBinder используется позже для вызова свойств или методов или чего-либо динамического.

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

person Icemanind    schedule 20.09.2012
comment
В этом случае вы не увидите никаких атрибутов, потому что локальные переменные не могут иметь атрибутов. - person svick; 21.09.2012
comment
@svick - Нет, но свойства могут иметь атрибуты, и в его примере Name является свойством. - person Icemanind; 21.09.2012
comment
Но тип этого свойства string, а не object с DynamicAttribute. - person svick; 21.09.2012
comment
О, вы правы.... свойство Name является строкой. Но объект Container будет украшен тегом атрибута [dynamic]. - person Icemanind; 21.09.2012
comment
Нет, не будет. У объектов нет атрибутов, как и у местных жителей. Атрибут необходим только в том случае, если вы обращаетесь к объекту dynamic извне, что невозможно, если это локальная переменная. - person svick; 21.09.2012