• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

C#4.0初探:dynamic 关键字

原作者: [db:作者] 来自: [db:来源] 收藏 邀请
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/<Main>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`1<!0> [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/<Main>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/<Main>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/<Main>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 <Main>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类型,检查编译器生成的静态类<Main>o__SiteContainer0的静态字段<>p__Site1是否为null,如果是,则对其赋值,赋值的内容在这里不详细研究。然后调用<>p__Site1进行操作。
这里会发现两个问题:赋值给dynamic的值赋给object类型,对于值类型会不会执行同样的操作,会执行装箱操作吗;编译器生成的静态类<Main>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(

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
(转)C# 泛型相关讲解发布时间:2022-07-13
下一篇:
使用Jsonp(JQ+C#)发布时间:2022-07-13
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap