По следам PDC. C# 4.0 Features. Dynamycally Typed Objects.
Категории: Разработка ПО
Тэги: .NET, C#, programming, Visual Studio
Небольшой обзор того, что нам готовит новая версия спецификации языка.
Смена первой цифры произошла благодаря четырём основным фичам:
- Dynamycally Typed Objects
- Optional and Named Prameters
- Improved COM Interoperability
- Co- and Conra-variance
Сегодня подробней рассмотрим первую.
Dynamycally Typed Objects
Динамическая система типов является, пожалуй, самым основным нововведением языка. Судя по лекциям с PDC 2008, c точки зрения архитектуры .NET добавился еще один компонент – DLR (dynamic object runtime), являющийся, по сути, надстройкой над CLR. С точки зрения языка добавилось ключевое слово dymanic. И одноименный тип добавился в .NET Type System.
Какое время назад Microsoft Research утверждала, что реализация будет происходить так: на момент компиляции объект будет не типизирован, а тэгирован, и на момент выполнения тег заменяется на свежевычесленный тип и вместо “неопределенного” типа подставляется “настоящий” и дальше все работает по старой схеме. Собственно DLR этим и должен заниматься.
Но что же происходит на самом деле?
Посмотрим как оно работает на примере C# сборки.
Пусть у нас есть класс:
namespace delme.ext { public class CDelmeExt { public string WhoAreYou(bool tellTruth) { if (tellTruth) { return "It's me, CDelmeExt, man!"; } return "Hey, I can't tell you, man!"; } } }
Сворачиваем его в сборку. Пусть она зовется “delme.ext.dll”.
Ранее для вызова метода WhoAreYou() класса CDelmeExt при динамической подгрузке сборки надо было создавать вариации на тему:
Assembly assembly = Assembly.LoadFrom(@"./delme.ext.dll"); object instance = assembly.CreateInstance("delme.ext.CDelmeExt"); Type delmeext = assembly.GetType("delme.ext.CDelmeExt"); object res = delmeext.InvokeMember("WhoAreYou", BindingFlags.InvokeMethod | BindingFlags.Default, null, instance, new object[] {true}); if(res is string) MessageBox.Show((string)res);
Теперь можно сделать проще:
Assembly assembly = Assembly.LoadFrom(@"./delme.ext.dll"); dynamic instance = assembly.CreateInstance("delme.ext.CDelmeExt"); object res = instance.WhoAreYuo(true); if (res is string) MessageBox.Show((string)res);
Первая мысль возникает о том, что dynamic очень похож на object, однако в этом случае не происходит “упаковки” (boxing) и “распаковки” (unboxing) типа. Он просто вычисляется на момент выполнения.
Однако взглянув на MSIL нас настигает откровение.
Как было. Случай с InvokeMember:
.entrypoint .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) // Code size 95 (0x5f) .maxstack 8 .locals init ([0] class [mscorlib]System.Reflection.Assembly 'assembly', [1] object 'instance', [2] class [mscorlib]System.Type delmeext, [3] object res, [4] object[] CS$0$0000) IL_0000: ldstr "./delme.ext.dll" IL_0005: call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::LoadFrom(string) IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: ldstr "delme.ext.CDelmeExt" IL_0011: callvirt instance object [mscorlib]System.Reflection.Assembly::CreateInstance(string) IL_0016: stloc.1 IL_0017: ldloc.0 IL_0018: ldstr "delme.ext.CDelmeExt" IL_001d: callvirt instance class [mscorlib]System.Type [mscorlib]System.Reflection.Assembly::GetType(string) IL_0022: stloc.2 IL_0023: ldloc.2 IL_0024: ldstr "WhoAreYou" IL_0029: ldc.i4 0x100 IL_002e: ldnull IL_002f: ldloc.1 IL_0030: ldc.i4.1 IL_0031: newarr [mscorlib]System.Object IL_0036: stloc.s CS$0$0000 IL_0038: ldloc.s CS$0$0000 IL_003a: ldc.i4.0 IL_003b: ldc.i4.1 IL_003c: box [mscorlib]System.Boolean IL_0041: stelem.ref IL_0042: ldloc.s CS$0$0000 IL_0044: callvirt instance object [mscorlib]System.Type::InvokeMember(string, valuetype [mscorlib]System.Reflection.BindingFlags, class [mscorlib]System.Reflection.Binder, object, object[]) IL_0049: stloc.3 IL_004a: ldloc.3
Тут все ясно, и прозрачно.
А вот что стало. Случай с динамическим типом выглядит так:
.method private hidebysig static void Main() cil managed { .entrypoint .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) // Code size 123 (0x7b) .maxstack 7 .locals init ([0] class [mscorlib]System.Reflection.Assembly 'assembly', [1] object 'instance', [2] object res, [3] bool CS$4$0000) IL_0000: nop IL_0001: ldstr "./delme.ext.dll" IL_0006: call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::LoadFrom(string) IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: ldstr "delme.ext.CDelmeExt" IL_0012: callvirt instance object [mscorlib]System.Reflection.Assembly::CreateInstance(string) IL_0017: stloc.1 IL_0018: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Func`4<class [System.Core]System.Scripting.Actions.CallSite,object,bool,object>> delme.Program/'<Main>o__SiteContainer0'::'<>p__Site1' IL_001d: brtrue.s IL_0047 IL_001f: call class [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder::GetInstance() IL_0024: ldc.i4.0 IL_0025: ldc.i4.0 IL_0026: ldstr "WhoAreYuo" IL_002b: ldtoken [mscorlib]System.Object IL_0030: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) IL_0035: ldnull IL_0036: newobj instance void [System.Core]Microsoft.CSharp.RuntimeBinder.CSharpCallPayload::.ctor(class [System.Core]Microsoft.CSharp.RuntimeBinder.RuntimeBinder, bool, bool, string, class [mscorlib]System.Type, class [mscorlib]System.Type[]) IL_003b: call class [System.Core]System.Scripting.Actions.CallSite`1<!0> class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Func`4<class [System.Core]System.Scripting.Actions.CallSite,object,bool,object>>::Create(class [System.Core]System.Scripting.Actions.CallSiteBinder) IL_0040: stsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Func`4<class [System.Core]System.Scripting.Actions.CallSite,object,bool,object>> delme.Program/'<Main>o__SiteContainer0'::'<>p__Site1' IL_0045: br.s IL_0047 IL_0047: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Func`4<class [System.Core]System.Scripting.Actions.CallSite,object,bool,object>> delme.Program/'<Main>o__SiteContainer0'::'<>p__Site1' IL_004c: ldfld !0 class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Func`4<class [System.Core]System.Scripting.Actions.CallSite,object,bool,object>>::Target IL_0051: ldsfld class [System.Core]System.Scripting.Actions.CallSite`1<class [System.Core]System.Func`4<class [System.Core]System.Scripting.Actions.CallSite,object,bool,object>> delme.Program/'<Main>o__SiteContainer0'::'<>p__Site1' IL_0056: ldloc.1 IL_0057: ldc.i4.1 IL_0058: callvirt instance !3 class [System.Core]System.Func`4<class [System.Core]System.Scripting.Actions.CallSite,object,bool,object>::Invoke(!0, !1, !2) IL_005d: stloc.2 IL_005e: ldloc.2 IL_005f: isinst [mscorlib]System.String IL_0064: ldnull IL_0065: cgt.un IL_0067: ldc.i4.0 IL_0068: ceq IL_006a: stloc.3 IL_006b: ldloc.3 IL_006c: brtrue.s IL_007a IL_006e: ldloc.2 IL_006f: castclass [mscorlib]System.String IL_0074: call valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string) IL_0079: pop IL_007a: ret } // end of method Program::Main
Во-первых, [1] object ‘instance’. Т.е. на уровне IL объект типа dynamic на самом деле System.Object. Во-вторых, ни одной новой конструкции CLR указывающей нам на то, что есть еще DLR, который вычисляет тип. Все реализовано новыми объектами, фактически расширение .NET Library. Чтобы сделать то же самое без dynamic достаточно написать так:
Assembly assembly = Assembly.LoadFrom(@"./delme.ext.dll"); object instance = assembly.CreateInstance("delme.ext.CDelmeExt"); CSharpCallPayload payload = new CSharpCallPayload( RuntimeBinder.GetInstance(), false, false, "WhoAreYou", typeof(object), null); var callSite = CallSite<Func<CallSite, object, bool, object>>.Create(payload); var func = callSite.Target; object str = func.Invoke(callSite, instance, true); if (str is string) MessageBox.Show((string)str);
Т.е. по сути, упростив вид конструкций на C#, мы получили нагромождение конструкций IL.
Стоит ли читабельность кода ставить в угоду производительности, а я уверен, что некоторый overhead даст знать о себе довольно быстро, решать разработчику.
Кому интересно – рабочий проект со всем вышеописанным для MS Visual Studio 2010:




.png)
Оставить комментарий