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

 更新时间:2020年6月25日 11:41  点击:1663

Main方法如下:

复制代码 代码如下:

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

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


    Console.ReadLine();
}


运行,结果如下:

clip_image002

使用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 就可以调用了。
 
image 
 
//这是编译器生成的代码://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);

 
看看如何调用的吧:
因为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是完全相同的,难道是为了实现简单?? 、幸亏还有延迟初始化,否则静态字段这么多,不知道会对系统产生什么影响 运行,
结果如下:

 clip_image002[4]

从这个例子也可以知道为什么dynamic会比反射的速度要快了。
1:if(p__Site1)==null,p__Site1==xxx,并且p__Site1是静态类中的静态字段,所以有缓存效果。
2:CallSite<T>.Target: 0 级缓存 - 基于站点历史记录专用的委托.
使用委托来调用,自然比反射又要快一些。
[!--infotagslink--]

相关文章

  • 关于C#基础知识回顾--反射(一)

    其实说白了,反射就是能知道我们未知类型的类型信息这么一个东西.没什么神秘可讲!反射的核心是System.Type。System.Type包含了很多属性和方法,使用这些属性和方法可以在运行时得到类型信息...2020-06-25
  • 关于C#反射 你需要知道的

    这篇文章主要介绍了C#反射的相关知识,文中讲解的非常详细,代码帮助大家更好的参考学习,感兴趣的朋友可以了解下...2020-06-25
  • C# 通过反射获取类型的字段值及给字段赋值的操作

    这篇文章主要介绍了C# 通过反射获取类型的字段值及给字段赋值的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-19
  • .NET/C#如何使用反射注册事件详解

    反射是.NET中的重要机制,下面这篇文章主要给大家介绍了关于.NET/C#如何使用反射注册事件的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧...2021-09-22
  • 前端JavaScript中的反射和代理

    这篇文章主要介绍的是前端JavaScript中的反射和代理,本文主要围绕JavaScript中的内置Reflect、JavaScript中的内置Proxy、Proxy实现观察者模式三个话题展开文章内容,需要的朋友可以参一考下...2021-10-22
  • asp.net反射简单应用实例

    这篇文章主要介绍了asp.net反射简单应用,结合完整实例形式分析了asp.net反射的原理与简单使用方法,需要的朋友可以参考下...2021-09-22
  • C#中 dynamic、Object和var之间的实际区别是什么?何时使用它们?

    dynamic关键字和动态语言运行时(DLR)是.Net 4.0中新增的功能。 什么是动态编程语言有时可以划分为静态类型化语言和动态类型化语言。C#和Java经常被认为是静...2020-05-11
  • php面向对象之反射功能与用法分析

    这篇文章主要介绍了php面向对象之反射功能与用法,结合实例形式简单分析了php5面向对象反射的概念及具体用法,需要的朋友可以参考下...2017-04-03
  • 深入解析Java反射之基础篇

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,这篇文章主要给大家介绍了关于Java反射之基础篇的相关资料,需要的朋友可以参考下...2021-11-18
  • C#中Dynamic和Dictionary性能比较

    开发中需要传递变参,考虑使用 dynamic 还是 Dictionary,dynamic 的编码体验显著优于 Dictionary,如果性能差距不大的话,我会选择使用dynamic。下面通过本文给大家详细介绍下C#中Dynamic和Dictionary性能比较,一起看看吧...2020-06-25
  • 关于C#基础知识回顾--反射(二)

    其实说白了,反射就是能知道我们未知类型的类型信息这么一个东西.没什么神秘可讲!反射的核心是System.Type。System.Type包含了很多属性和方法,使用这些属性和方法可以在运行时得到类型信息...2020-06-25
  • C++反射的一种实现方法详解

    这篇文章主要给大家介绍了关于C++反射的一种实现方法,文中通过示例代码介绍的非常详细,对大家学习或者使用C++具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-04-25
  • 浅析C# Dynamic关键字

    这篇文章主要介绍了C# Dynamic关键字的相关资料,文中讲解非常细致,对大家学习C# Dynamic关键字有所帮助,感兴趣的朋友可以了解下...2020-11-03
  • C#泛型和反射实例解析

    这篇文章主要介绍了C#泛型和反射实例解析,对于C#初学者理解泛型和反射有很好的帮助借鉴作用,需要的朋友可以参考下...2020-06-25
  • Asp.net中使用DapperExtensions和反射来实现一个通用搜索

    这篇文章主要介绍了Asp.net中使用DapperExtensions和反射来实现一个通用搜索功能,非常不错,具有参考解决价值,需要的朋友可以参考下...2021-09-22
  • java 利用反射获取内部类静态成员变量的值操作

    这篇文章主要介绍了java 利用反射获取内部类静态成员变量的值操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-04
  • 深入反射生成数组的详解

    本篇文章是对反射生成数组进行了详细的分析介绍,需要的朋友参考下...2020-06-25
  • 用户控件(ASCX)向网页(ASPX)传值使用反射实现

    用户控件向网页传递值,网上的方法有很多,本文尝试一下使用反射来实现,感兴趣的朋友可以参考下哈,希望可以帮助到你...2021-09-22
  • C# 反射与dynamic最佳组合示例代码

    这篇文章主要介绍了C# 反射与dynamic最佳组合示例代码,需要的朋友可以参考下...2020-06-25
  • C#反射实例学习及注意内容

    C#反射的入门学习首先要明白C#反射提供了封装程序集、模块和类型的对象等等需要的朋友可以参考下...2021-09-22