C# Dynamic关键字之:dynamic为什么比反射快的详解

Main方法如下:


代码如下:

static void Main(string[] args)
{
    dynamic str = "abcd";
    Console.WriteLine(str.Length);

Console.WriteLine();
    Console.WriteLine(str.Substring(1));

Console.ReadLine();
}

运行,结果如下:


使用reflactor 反编译下,可以看到:

完整代码如下:

代码如下:

private static void Main(string[] args)
{
      object obj1 = "abcd";
      if (Program.<Main>o__SiteContainer0.<>p__Site1 == null)
      {
            Program.<Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, Type, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "WriteLine", null, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
      }
      if (Program.<Main>o__SiteContainer0.<>p__Site2 == null)
      {
            Program.<Main>o__SiteContainer0.<>p__Site2 = CallSite<Func<CallSite, object, object>>.Create(Binder.GetMember(CSharpBinderFlags.None, "Length", typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
      }
      Program.<Main>o__SiteContainer0.<>p__Site1.Target(Program.<Main>o__SiteContainer0.<>p__Site1, typeof(Console), Program.<Main>o__SiteContainer0.<>p__Site2.Target(Program.<Main>o__SiteContainer0.<>p__Site2, obj1));
      Console.WriteLine();
      if (Program.<Main>o__SiteContainer0.<>p__Site3 == null)
      {
            Program.<Main>o__SiteContainer0.<>p__Site3 = CallSite<Action<CallSite, Type, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "WriteLine", null, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
      }
      if (Program.<Main>o__SiteContainer0.<>p__Site4 == null)
      {
            Program.<Main>o__SiteContainer0.<>p__Site4 = CallSite<Func<CallSite, object, int, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "Substring", null, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null) }));
      }
      Program.<Main>o__SiteContainer0.<>p__Site3.Target(Program.<Main>o__SiteContainer0.<>p__Site3, typeof(Console), Program.<Main>o__SiteContainer0.<>p__Site4.Target(Program.<Main>o__SiteContainer0.<>p__Site4, obj1, 1));
      Console.ReadLine();
}

首先编译器会自动生成一个静态类:如下:


代码如下:

[CompilerGenerated]
private static class <Main>o__SiteContainer0
{
      // Fields
      public static CallSite<Action<CallSite, Type, object>> <>p__Site1;
      public static CallSite<Func<CallSite, object, object>> <>p__Site2;
      public static CallSite<Action<CallSite, Type, object>> <>p__Site3;
      public static CallSite<Func<CallSite, object, int, object>> <>p__Site4;
}

为什么这里有四个CallSite<T>的对象呢?在我们的代码中

Console.WriteLine(str.Length);
Console.WriteLine();
Console.WriteLine(str.Substring(1));
一共使用了四次dynamic对象。1:Console.WriteLine(dynamic); str.Length返回dynamic2:dynamic.Length;3:Console.WriteLine(dynamic); str.Substring 返回dynamic4:dynamic.Substring(1); 1,2,3,4,分别对应上面的<>p_Site1,2,3,4;

因为1,3 都是无返回值的,所以是Action, 2,4都有返回值,所以是Func. 看上面的代码可能还不清楚,让我们手动的生成下代码吧:新建类SiteContainer 来取代编译器自动生成的类。


代码如下:

[CompilerGenerated]
public static class SiteContainer
{
  // Fields
  public static CallSite<Action<CallSite, Type, object>> p__Site1;
  public static CallSite<Func<CallSite, object, object>> p__Site2;
  public static CallSite<Action<CallSite, Type, object>> p__Site3;
  public static CallSite<Func<CallSite, object, int, object>> p__Site4;
}

接着看下初始化p__Site1时的方法吧:


代码如下:

if (SiteContainer.p__Site1 == null)
{
    CallSiteBinder csb= Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
        CSharpBinderFlags.ResultDiscarded,
        "WriteLine", null, typeof(Program),
        new CSharpArgumentInfo[]
        {
            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType,null),
            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null)
        });
    SiteContainer.p__Site1 = CallSite<Action<CallSite, Type, object>>.Create(csb);
}

InvokeMember方法的签名:public static CallSiteBinder InvokeMember(CSharpBinderFlags flags, string name, IEnumerable<Type> typeArguments, Type context, IEnumerable<CSharpArgumentInfo> argumentInfo); 1:在这里CSharpBinderFlags传递的是ResultDiscarded,代表结果被丢弃,   所以可以绑定到一个void的返回方法中。2:name传递的是”WriteLine”,要调用的方法的名称。3:typeArguments.类型参数的列表,传递null。4:context: 用于指示此操作的使用位置的 System.Type,在这里是Program5:argumentInfo:参数信息,   接着看看p__Site2如何初始化的吧:


代码如下:

if (SiteContainer.p__Site2 == null)
{
    CallSiteBinder csb = Microsoft.CSharp.RuntimeBinder.Binder.GetMember(
        CSharpBinderFlags.None, "Length", typeof(Program),
        new CSharpArgumentInfo[]
        {
            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
        });

SiteContainer.p__Site2 = CallSite<Func<CallSite, object, object>>.Create(csb);
}

可以看到,和p__Site1不同的是,调用的是GetMember方法。
 
既然有了两个CallSite<T>的对象,那么它们又是如何调用的呢??
使用CallSite<T>.Target 就可以调用了。
 
 
 
//这是编译器生成的代码://SiteContainer.p__Site1.Target(SiteContainer.p__Site1, typeof(Console), // SiteContainer.p__Site2.Target(SiteContainer.p__Site2, obj1) //); var pSite2Result = SiteContainer.p__Site2.Target(SiteContainer.p__Site2, obj1); SiteContainer.p__Site1.Target(SiteContainer.p__Site1, typeof(Console), pSite2Result);

.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }

 
看看如何调用的吧:
因为SiteContainer.p__Site2,是调用Length属性
首先调用p__Site2的target方法,执行p__Site2,对象是obj1.
dlr 就会调用obj1.Length,并返回结果,所以pSite2Result=4;
接着调用p__Site1的target,来调用Console类的WriteLine方法,参数是pSite2Result.所以输出4.
 
最后来看下dynamic是如何调用Substring方法的:

Substring方法对应的是p__Site4,因为Substring方法传递了个参数1,并且有返回值,所以

p__Site4对象是:

public static CallSite<Func<CallSite, object, int, object>> p__Site4;
初始化:


代码如下:

if (SiteContainer.p__Site4 == null)
{
    CallSiteBinder csb = Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
        CSharpBinderFlags.None, "Substring", null, typeof(Program),
        new CSharpArgumentInfo[]
        {
            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant
            | CSharpArgumentInfoFlags.UseCompileTimeType, null)                       
        });
    SiteContainer.p__Site4 = CallSite<Func<CallSite, object, int, object>>.Create(csb);
}

基本和上面的p__Site1类似,只是参数信息是:CSharpArgumentInfoFlags.Constant \

因为调用了Substring(1).在编译的时候会传递1进去,而1是常量。 调用如下:


代码如下:

var subStringResult = SiteContainer.p__Site4.Target(SiteContainer.p__Site4, obj1, 1);
SiteContainer.p__Site1.Target(SiteContainer.p__Site1, typeof(Console), subStringResult);

解释同上。

完整的Main函数代码如下:


代码如下:

static void Main(string[] args)
{
    object obj1 = "abcd";

if (SiteContainer.p__Site1 == null)
    {
        CallSiteBinder csb = Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
            CSharpBinderFlags.ResultDiscarded,
            "WriteLine", null, typeof(Program),
            new CSharpArgumentInfo[]
            {
                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType,null),
                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null)
            });
        SiteContainer.p__Site1 = CallSite<Action<CallSite, Type, object>>.Create(csb);
    }

if (SiteContainer.p__Site2 == null)
    {
        CallSiteBinder csb = Microsoft.CSharp.RuntimeBinder.Binder.GetMember(
            CSharpBinderFlags.None, "Length", typeof(Program),
            new CSharpArgumentInfo[]
            {
                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
            });

SiteContainer.p__Site2 = CallSite<Func<CallSite, object, object>>.Create(csb);
    }

if (SiteContainer.p__Site4 == null)
    {
        CallSiteBinder csb = Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
            CSharpBinderFlags.None, "Substring", null, typeof(Program),
            new CSharpArgumentInfo[]
            {
                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null)    
            });
        SiteContainer.p__Site4 = CallSite<Func<CallSite, object, int, object>>.Create(csb);
    }

var lengthResult = SiteContainer.p__Site2.Target(SiteContainer.p__Site2, obj1);
    SiteContainer.p__Site1.Target(SiteContainer.p__Site1, typeof(Console), lengthResult);

var subStringResult = SiteContainer.p__Site4.Target(SiteContainer.p__Site4, obj1, 1);
    SiteContainer.p__Site1.Target(SiteContainer.p__Site1, typeof(Console), subStringResult);

Console.ReadLine();
}

在这里,我没有使用p__Site3,因为p__Site3和p__Site1相同,不过为什么微软会生成4个CallSite<T>对象,因为1 和3是完全相同的,难道是为了实现简单?? 、幸亏还有延迟初始化,否则静态字段这么多,不知道会对系统产生什么影响 运行,
结果如下:

从这个例子也可以知道为什么dynamic会比反射的速度要快了。
1:if(p__Site1)==null,p__Site1==xxx,并且p__Site1是静态类中的静态字段,所以有缓存效果。
2:CallSite<T>.Target: 0 级缓存 - 基于站点历史记录专用的委托.
使用委托来调用,自然比反射又要快一些。.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }

(0)

相关推荐

  • c#反射调用方法示例

    获取方法的相关信息的两种形式 反射是一种允许用户获得类信息的C#功能,Type对象映射它代表的底层对象: 在.Net 中, 一旦获得了Type对象,就可以使用GetMethods()方法获取此类型支持的方法列表:该方法的两种形式: MethodInfo [] GetMethods() MethodInfo [] GetMethods(BindingFlags bindingflas)  :它的参数带有一些限制 BindingFlags  是一个枚举 枚举成员 [DeclaredOnly,Inst

  • C#反射实例学习及注意内容

    C#反射的入门学习首先要明白C#反射提供了封装程序集.模块和类型的对象等等.那么这样可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性.如果代码中使用了属性,可以利用反射对它们进行访问. MSDN描述: 反射通常具有以下用途: 使用 Assembly 定义和加载程序集,加载在程序集清单中列出的模块,以及从此程序集中查找类型并创建该类型的实例. 使用 Module 发现以下信息:包含模块的程序集以及模块中的类等.您还可以获取在模块上定义的所有全

  • c#反射表达式树模糊搜索示例

    复制代码 代码如下: public static Expression<Func<T, bool>> GetSearchExpression<T>(string SearchString)        {            Expression<Func<T, bool>> filter = null; if (string.IsNullOrEmpty(SearchString)) return null;            var l

  • C#泛型和反射实例解析

    C#中的泛型和反射经常是一起工作的,因此这里就一次性的加以介绍了. 由于c#是强类型语言,一般来说函数的返回类型和参数的类型都是一早写好的,这也就造成了很多时候不像js那样方便使用,不够灵话. 因此就有了这个泛型,它可以让你的函数和参数在调用的时候才决定类型.如下例所示: public T abc<T>(T word) { return word; return default(T); //关键字default可以对引用类型返回nullAble,int类型返回0,初始化一个T的感觉啦 } ab

  • C#通过反射创建自定义泛型

    本文以实例形式讲述了C#通过反射创建自定义泛型的实现方法,分享给大家供大家参考.具体如下: 比如有这样一个泛型:Demo.GenericsSimple<T,TT> 我想要通过反射创建一个Demo.GenericsSimple<string,int>的实例可以通过下面的格式进行创建: System.Reflection.Assembly.GetExecutingAssembly().CreateInstance("命名空间.User`形参数量N[[1形参类型全名,形参类型所

  • c#使用反射调用类型成员示例

    在实际的工作中直接使用反射的机会比较少,有印象的就是一次自己做的WinForms小工具的时候利用反射来动态获取窗体上的每个控件,并且为必要的控件动态添加注册事件.因为刚入职新公司,为了更快的了解公司的业务.和开发习惯,先和现在公司同事一起修改现有系统的一些小Bug.在Tester提交的Bug中有一个是对GridView进行动态的排序--点击一个列时使用该列作为条件进行排序(PS:点击一个列时前台会将该列的字符串(该字符串是)传到后台的方法中). 使用反射的原因 为什么会选择使用反射呢?在项目中我

  • c#测试反射性能示例

    Activator.CreateInstance和AssemblyCreateInstance性能测试 复制代码 代码如下: using System;using System.Collections.Generic;using System.Diagnostics;using System.Linq;using System.Security.Cryptography;using System.Text;using HelloWorld.ServiceReference1;using Syst

  • C#反射的一些应用

    对于反射贫道也是很陌生的,所以趁现在有时间就把反射看了一下,记下笔记!!!反射的定义:反射(Reflection)是.NET中的重要机制,通过放射,可以在运行时获得.NET中每一个类型(包括类.结构.委托.接口和枚举等)的成员,包括方法.属性.事件,以及构造函数等.还可以获得每个成员的名称.限定符和参数等.有了反射,即可对每一个类型了如指掌.如果获得了构造函数的信息,即可直接创建对象,即使这个对象的类型在编译时还不知道.  1,导入using System.Reflection;  2,Asse

  • C#中使用反射获取结构体实例及思路

    复制代码 代码如下: static void Main(string[] args){    Type type = typeof(MyObject);    object obj = type.GetConstructor(Type.EmptyTypes).Invoke(null);    Console.WriteLine(obj);} class MyObject{ } 之前我一直没有发现原来结构是不可以这样实例化的 换种方式,似乎结构体使用反射无法得到其构造函数ConstructorIn

  • c#反射机制学习和利用反射获取类型信息

    1..NET可执行应用程序结构 程序代码在编译后生成可执行的应用,我们首先要了解这种可执行应用程序的结构. 应用程序结构分为应用程序域-程序集-模块-类型-成员几个层次,公共语言运行库加载器管理应用程序域,这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局. 程序集包含模块,而模块包含类型,类型又包含成员,反射则提供了封装程序集.模块和类型的对象.我们可以使用反射动态地创建类型的实例,将类型绑定到现有对象或从现有对象中获取类型,然后调用类型的方法或访问其字段

  • C#反射之基础应用实例总结

    本文将反射的东西整理了一下 , 提供了最全面的东西 , 当然也是基础的东西 , 在学好了这一切的基础上 , 大家可以学习反射的具体插件等应用 首先我们建立一个类库 , 将它生成为 reflectPrj .dll, 复制代码 代码如下: using System; using System.Collections.Generic; using System.Text;   namespace reflectPrj {     /// <summary>     /// 接口     /// &l

  • C#反射在实际应用中的实例代码

    反射提供了封装程序集.模块和类型的对象(Type 类型).可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性.如果代码中使用了属性,可以利用反射对它们进行访问. 下面我就以一个事例来说明反射在项目中的使用方法. 大体分为三个步骤: 第一步,在web.config配置如下代码(目的是为了动态的去修改所需分析的dll) 复制代码 代码如下: <appSettings>      <add key="BizAssembly&quo

  • C#反射(Reflection)对类的属性get或set值实现思路

    近段时间,有朋友叫Insus了解一下反射(Reflection)方面的知识,反射提供了封装程序集.模块和类型的对象(Type类型).可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性.如果代码中使用了属性,可以利用反射对它们进行访问. 下面的例子,是Insus练习对一个类别的属性进行set和get值. 首先写一个类,再写一个可读写的属性: 复制代码 代码如下: using System; using System.Collections.Ge

随机推荐