艾伟:C#4.0初探:dynamic 关键字

C#新增了dynamic关键字,正因为这一个小小的关键字,C#动态特性向前迈进了一大步。
dynamic是一个类型关键字,声明为dynamic的类型与"静态类型"(这里的静态类型是指编译时确定的类型,下同)相比最大的特点它是"动态类型",它会运行时尝试调用方法,这些方法的存在与否不是在编译时检查的,而是在运行时查找,如果方法存在并且参数正确,会正常调用,否则会抛出Microsoft.CSharp.RuntimeBinder.RuntimeBinderException异常。

看一个最简单的示例:

using System;

namespace Xianfen.Net.TestDynamic
{
    class Program
    {
        static void Main()
        {
            dynamic d = Console.Out;
            dynamic a;
            a = new Int32();
            int b = a;
            a++;
            a--;

            d.WriteLine("http://www.xianfen.net/");
            d.WriteLine(d.GetType());
            d.writeln("test"); //抛出Microsoft.CSharp.RuntimeBinder.RuntimeBinderException异常
        }
    }
}

对dynamic类型的操作只能有以下几种:
·赋值
·方法调用
·自增
·自减
·接受"静态类型"的构造器创建的对象

与关键字var的比较
从表面上看,dynamic与var关键字的用法很像,但实质上有本质区别。
var关键字被称为:隐含类型局部变量(Local Variable Type Inference),var只能用作局部变量,不能用于字段、参数等;声明的同时必须初始化;初始化时类型就已经明确了,并且不能再被赋值不能进行隐式类型转换的类型的数据;编译时编译器会对var定义的变量进行类型推断,这时变量的类型已经被确定。
dynamic可用于类型的字段,方法参数,方法返回值,可用于泛型的类型参数等;可以赋值给或被赋值任何类型并且不需显式的强制类型转换,因为这些是运行时执行的,这要得益于dynamic类型的动态特性。

与反射的比较
首先能看到的是,dynamic与反射相比,执行相同操作所需的代码少的多。
如调用类Me中的GetName()方法。

class Me
{
    public string Blog { get; set; }

    public string GetName()
    {
        return "Zhenxing Zhou";
    }
}

用反射调用GetName()方法:

Assembly a = Assembly.GetExecutingAssembly();
object instance = a.CreateInstance("Xianfen.Net.TestDynamic.Me");
Type type = instance.GetType();
MethodInfo mi = type.GetMethod("GetName");
object result = mi.Invoke(instance, null);

同样的dynamic调用:

dynamic myInfo = new Me();
string result = myInfo.GetName();

dynamic类型与反射相比能进行的操作要少的多。
目前dynamic类型对属性调用是不可用的,但我们知道,属性生成IL时,对属性的读或写会生成对应的在属性名前加上get_或set_前缀生成相应的方法,尝试调用两个方法来访问属性:

dynamic myInfo = new Me();
myInfo.set_Blog("http://www.xianfen.net/");
string result = myInfo.get_Blog();

会抛出异常,提示找不到get/set_Blog方法。这点比较遗憾,同样,对有参属性的访问也是不行的。
反射还可以访问私有方法字段以及其它类型成员及取得类型及类型成员的信息等。

dynamic类型的效率
效率问题应该是大家很关心的,我的感觉:不要对动态语言有很高的效率抱有太大的希望,但另一方面,算法的设计对效率的影响非常大,功能与性能经常存在一个平衡点。
要分析其效率,就要看看编译后内部都干了些啥,方法是写些简单的代码,查看IL。
代码:

using System;

namespace Xianfen.Net.TestDynamic
{
    class Program
    {
        static void Main()
        {
            dynamic d = "str";
            d.ToString();
        }
    }
}

对应的IL代码:

.class private auto ansi beforefieldinit Program
    extends [mscorlib]System.Object
{
    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
    {
        .maxstack 8
        L_0000: ldarg.0
        L_0001: call instance void [mscorlib]System.Object::.ctor()
        L_0006: ret
    }

    .method private hidebysig static void Main() cil managed
    {
        .entrypoint
        .maxstack 9
        .locals init (
            [0] object d,
            [1] class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo[] CS$0$0000)
        L_0000: ldstr "str"
        L_0005: stloc.0
        L_0006: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`2<class [System.Core]System.Runtime.CompilerServices.CallSite, object>> Xianfen.Net.TestDynamic.Program/o__SiteContainer0::<>p__Site1
        L_000b: brtrue.s L_003f
        L_000d: ldc.i4.0
        L_000e: ldstr "ToString"
        L_0013: ldtoken Xianfen.Net.TestDynamic.Program
        L_0018: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
        L_001d: ldnull
        L_001e: ldc.i4.1
        L_001f: newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
        L_0024: stloc.1
        L_0025: ldloc.1
        L_0026: ldc.i4.0
        L_0027: ldc.i4.0
        L_0028: ldnull
        L_0029: newobj instance void [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::.ctor(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)
        L_002e: stelem.ref
        L_002f: ldloc.1
        L_0030: newobj instance void [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder::.ctor(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpCallFlags, string, class [mscorlib]System.Type, class [mscorlib]System.Collections.Generic.IEnumerable`1<class [mscorlib]System.Type>, class [mscorlib]System.Collections.Generic.IEnumerable`1<class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)
        L_0035: call class [System.Core]System.Runtime.CompilerServices.CallSite`10> [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`2<class [System.Core]System.Runtime.CompilerServices.CallSite, object>>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)
        L_003a: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`2<class [System.Core]System.Runtime.CompilerServices.CallSite, object>> Xianfen.Net.TestDynamic.Program/o__SiteContainer0::<>p__Site1
        L_003f: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`2<class [System.Core]System.Runtime.CompilerServices.CallSite, object>> Xianfen.Net.TestDynamic.Program/o__SiteContainer0::<>p__Site1
        L_0044: ldfld !0 [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`2<class [System.Core]System.Runtime.CompilerServices.CallSite, object>>::Target
        L_0049: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`2<class [System.Core]System.Runtime.CompilerServices.CallSite, object>> Xianfen.Net.TestDynamic.Program/o__SiteContainer0::<>p__Site1
        L_004e: ldloc.0
        L_004f: callvirt instance void [mscorlib]System.Action`2<class [System.Core]System.Runtime.CompilerServices.CallSite, object>::Invoke(!0, !1)
        L_0054: ret
    }

    .class abstract auto ansi sealed nested private beforefieldinit o__SiteContainer0
        extends [mscorlib]System.Object
    {
        .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
        .field public static class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`2<class [System.Core]System.Runtime.CompilerServices.CallSite, object>> <>p__Site1

    }
}

可以看出生成的IL代码确实不美观,不过大体能看出端倪。为了方便查看,用Reflector查看,把反编译结果设置为.net2.0,代码清晰多了:

01. internal class Program
02. {
03.     // Methods
04.     private static void Main()
05.     {
06.         object d = "str";
07.         if (<Main>o__SiteContainer0.<>p__Site1 == null)
08.         {
09.             <Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, object>>.
10. Create(new CSharpInvokeMemberBinder(CSharpCallFlags.None, "ToString", typeof(Program),
11. null, new CSharpArgumentInfo[] { new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null) }));
12.         }
13.         <Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, d);
14.     }
15. // Nested Types
16.     [CompilerGenerated]
17.     private static class <Main>o__SiteContainer0
18.     {
19.         // Fields
20.         public static CallSite<Action<CallSite, object>> <>p__Site1;
21.     }
22. }

06行先把赋值给dynamic的值赋给object类型,检查编译器生成的静态类o__SiteContainer0的静态字段<>p__Site1是否为null,如果是,则对其赋值,赋值的内容在这里不详细研究。然后调用<>p__Site1进行操作。
这里会发现两个问题:赋值给dynamic的值赋给object类型,对于值类型会不会执行同样的操作,会执行装箱操作吗;编译器生成的静态类o__SiteContainer0的静态字段<>p__Site1应该是缓存作用。这两个问题稍后验证。

1)对值类型进行的操作
如下代码:

using System;

namespace Xianfen.Net.TestDynamic
{
    class Program
    {
        static void Main()
        {
            dynamic d = 5;
            d.ToString();
        }
    }
}

反编译代码:

internal class Program
{
    // Methods
    private static void Main()
    {
        object d = 5;
        if (<Main>o__SiteContainer0.<>p__Site1 == null)
        {
            <Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, object>>.
Create(new CSharpInvokeMemberBinder(CSharpCallFlags.None, "ToString", typeof(Program),
null, new CSharpArgumentInfo[] { new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null) }));
        }
        <Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, d);
    }

    // Nested Types
    [CompilerGenerated]
    private static class <Main>o__SiteContainer0
    {
        // Fields
        public static CallSite<Action<CallSite, object>> <>p__Site1;
    }
}

可见确实对值类型进行了装箱操作,效率可想而知。

2)编译器生成的缓存类
代码如下:

using System;

namespace Xianfen.Net.TestDynamic
{
    class Program
    {
        static void Main()
        {
            dynamic d = 5;
            d.ToString();
            d.ToString();
        }
    }
}

反编译的代码:

internal class Program
{
    // Methods
    private static void Main()
    {
        object d = 5;
        if (<Main>o__SiteContainer0.<>p__Site1 == null)
        {
            <Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, object>>.Create(new CSharpInvokeMemberBinder(CSharpCallFlags.None, "ToString", typeof(Program), null, new CSharpArgumentInfo[] { new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null) }));
        }
        <Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, d);
        if (<Main>o__SiteContainer0.<>p__Site2 == null)
        {
            <Main>o__SiteContainer0.<>p__Site2 = CallSite<Action<CallSite, object>>.Create(new CSharpInvokeMemberBinder(CSharpCallFlags.None, "ToString", typeof(Program), null, new CSharpArgumentInfo[] { new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null) }));
        }
        <Main>o__SiteContainer0.<>p__Site2.Target(<Main>o__SiteContainer0.<>p__Site2, d);
    }

    // Nested Types
    [CompilerGenerated]
    private static class <Main>o__SiteContainer0
    {
        // Fields
        public static CallSite<Action<CallSite, object>> <>p__Site1;
        public static CallSite<Action<CallSite, object>> <>p__Site2;
    }
}

代码调用了ToString方法,但编译器生成了两份缓存。

如果是在循环中:
代码:

using System;

namespace Xianfen.Net.TestDynamic
{
    class Program
    {
        static void Main()
        {
            dynamic d = 5;

            for (int i = 0; i < 100; i++)
            {
                d.ToString();
            }
        }
    }
}

反编译代码:

internal class Program
{
    // Methods
    private static void Main()
    {
        object d = 5;
        for (int i = 0; i < 100; i++)
        {
            if (<Main>o__SiteContainer0.<>p__Site1 == null)
            {
                <Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, object>>.Create(new CSharpInvokeMemberBinder(CSharpCallFlags.None, "ToString", typeof(Program), null, new CSharpArgumentInfo[] { new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null) }));
            }
            <Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, d);
        }
    }

    // Nested Types
    [CompilerGenerated]
    private static class <Main>o__SiteContainer0
    {
        // Fields
        public static CallSite<Action<CallSite, object>> <>p__Site1;
    }
}

可见在循环中,相同的操作做了一次缓存;但非循环环境下,调用一次会缓存一次,猜测原因是,重复调用一个方法的次数不会太多,并且很多情况准确查找起来比较困难。
(以上代码在VS2010Beta1下测试通过)
URL: http://www.xianfen.net/
Author: Zhenxing Zhou

时间: 2024-07-31 10:40:17

艾伟:C#4.0初探:dynamic 关键字的相关文章

C#4.0初探:dynamic 关键字

C#新增了dynamic关键字,正因为这一个小小的关键字,C#动态特性向前迈进了一大步.dynamic是一个类型关键字,声明为dynamic的类型与"静态类型"(这里的静态类型是指编译时确定的类型,下同)相比最大的特点它是"动态类型",它会运行时尝试调用方法,这些方法的存在与否不是在编译时检查的,而是在运行时查找,如果方法存在并且参数正确,会正常调用,否则会抛出Microsoft.CSharp.RuntimeBinder.RuntimeBinderException

C# 动态语言特性,dynamic 关键字研究

原文:C# 动态语言特性,dynamic 关键字研究 1       动态语言简介 支持动态特性的语言现在大行其道,并且有继续增长的趋势.比如 Ruby 和 Python, 还有天王级的巨星 --- JavaScript. 现在一个程序员说自己对 JavaScript 根本没使用过,别人一定把你当成从火星回来的吧! 很多使用过 JavaScript 的程序员,刚开始对其动态特性深恶痛绝,欲除之而后快,但是一旦熟悉这个语言以后,又会发疯般的爱上她(我的野蛮女友). 以创建一个"人"为例,

了解 C# “.NET研究”4 中的 Dynamic 关键字

dynamic 关键字和动态语言运行时 (DLR) 是 C# 4 和 Microsoft .NET Framework 4 中的重大新增功能. 这些功能在宣布时就引起了人们的极大兴趣,并伴随着许多疑问. 同时人们也给出了很多答案,但这些答案现在已散布于各种文档以及各种技术博客和文章之中. 这样,人们在各种论坛和会议上总是一遍又一遍地提出相同的问题. 本文全面概述了 C# 4 中新增的动态功能,并且深入探讨了这些功能如何同其他语言和框架功能(例如反射或隐式类型化变量)一起使用. 鉴于已有大量信息可

一起谈.NET技术,了解 C# 4 中的 Dynamic 关键字

dynamic 关键字和动态语言运行时 (DLR) 是 C# 4 和 Microsoft .NET Framework 4 中的重大新增功能. 这些功能在宣布时就引起了人们的极大兴趣,并伴随着许多疑问. 同时人们也给出了很多答案,但这些答案现在已散布于各种文档以及各种技术博客和文章之中. 这样,人们在各种论坛和会议上总是一遍又一遍地提出相同的问题. 本文全面概述了 C# 4 中新增的动态功能,并且深入探讨了这些功能如何同其他语言和框架功能(例如反射或隐式类型化变量)一起使用. 鉴于已有大量信息可

C# 4.0初探

C#新增了dynamic关键字,正因为这一个小小的关键字,C#动态特性向前迈进 了一大步. dynamic是一个类型关键字,声明为dynamic的类型与" 静态类型"(这里的静态类型是指编译时确定的类型,下同)相比最大的特 点它是"动态类型",它会运行时尝试调用方法,这些方法的存在与否 不是在编译时检查的,而是在运行时查找,如果方法存在并且参数正确,会正常 调用,否则会抛出Microsoft.CSharp.RuntimeBinder.RuntimeBinderExc

.NET 4.0 之 Dynamic 动态类型

一..NET4.0主要新特性 .NET4.0在.Net3.5基础上新增的主要特性有:可选参数.命名参数和Dynamic.具体请阅生鱼片的这篇博文.这里我们着重讲解C#4.0的Dynamic特性,对于其他特性大家可以在VS2010内尝试一下.总之.Net在不断进步中. 二.ExpandoObject普通应用 ExpandoObject 类,"需引用System.Dynamic命名空间" .请看以下代码: dynamic Customer = new ExpandoObject(); Cu

一起谈.NET技术,.NET 4.0 之 Dynamic 动态类型

一..NET4.0主要新特性 .NET4.0在.Net3.5基础上新增的主要特性有:可选参数.命名参数和Dynamic.具体请阅生鱼片的这篇博文.这里我们着重讲解C#4.0的Dynamic特性,对于其他特性大家可以在VS2010内尝试一下.总之.Net在不断进步中. 二.ExpandoObject普通应用 ExpandoObject 类,"需引用System.Dynamic命名空间" .请看以下代码: dynamic Customer = new ExpandoObject();Cus

.NET“.NET研究” 4.0 之 Dynamic 动态类型

一..NET4.0主要新特性 .NET4.0在.Net3.5基础上新增的主要特性有:可选参数.命名参数和Dynamic.具体请阅生鱼片的这篇博文.这里我们着重讲解C#4.0的Dynamic特性,对于其他特性大家可以在VS2010内尝试一下.总之.Net在不断进步中. 二.ExpandoObject普通应用 ExpandoObject 类,"需引用System.Dynamic命名空间" .请看以下代码: dynamic Customer = new ExpandoObject();Cus

C#4.0的dynamic用法(一)——巧用反射

在平时做框架架构设计的时候,头疼之一的是处处得采用反射,但有了C#4.0,发现dynamic完全可以取代反射,这个功能让我有些激动,立马在VS2010将日志跟踪器框架里的第一个反射的代码升级到C#4.0,结果一点都不令人失望,代码简化了很多. 先看看用dynamic替换反射后的代码吧: 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using Syst