• Dynamic Binding
    • 动态绑定

      • Binding:解析Type,member,operation的过程.
      • 动态绑定将Binding从编译时延迟到运行时进行.
    • 场景
      • 编译时,程序员知道特定的function,member,operation的存在,而Compiler不知道.
 dynamic d = GetSomeObject();
d.Quack();

dynamic_obj

      • 期望在运行时,d类型会含有Quack().所以,编译器推迟d和Quack()的绑定到运行时.
    • dynamic
      • dynamic与object关键字一样,不提供任何对象类型的信息.
      • 但是,区别在于,如果Compiler遇见dynamic,会将语句包装起来,然后在运行时进行Binding动作.
    • Custom Binding
      • 发生在一个实现了IDynamicMetaObjectProvider的动态对象上.
      • 大部分动态对象都是从实现了DLR的动态语言中获得,但是也可以自定义.
      • 动态对象可以直接地控制所有对它的消息调用的处理过程.
    • Language Binding
      • 发生在一个动态对象没有实现IDMOP接口时.
      • 适用于需要绕过基础类型的缺陷和在类型上先天的限制.
      • 比如,数值类型没有一个统一的接口.可以使用dynamic来对应所有的类型.
      • 它绕过了编译时类型安全的检查,但是不能绕过成员访问规则(反射可以).
      • 动态绑定会造成一定的性能损失.但是DLR有cache机制,针对同一动态语句的调用会被优化.
    • RuntimeBinderException
      • 如果绑定失败,抛出该异常.
    • Dynamic的运行时描述
      • Object和Dynamic有很深的相等性

        • typeof (dynamic) == typeof (object)
        • typeof (List<dynamic>) == typeof (List<object>)
        • typeof (dynamic[]) == typeof (object[])
        • Dynamic引用和Object引用一样,可以指向任何的类型(pointer除外).
      • 这样,可以在支持Dynamic的语言中使用Dynamic特性,而不支持时使用Object.
         public class Test
        {
        public dynamic Foo;
        }
        等同于
        public class Test
        {
        [System.Runtime.CompilerServices.DynamicAttribute]
        public object Foo;
        }
    • Dynamic转换
      • 动态类型和其它类型之间,会含有隐式转换.
      • 转换成功的前提,是动态对象必须能够与目标对象类型是兼容的.
      • int i = ;
        dynamic d = i;
        long j = d; // No cast required (implicit conversion) int i = ;
        dynamic d = i;
        short j = d; // throws RuntimeBinderException
    • Dynamic表达式
      • 试图使用一个返回结果为void的表达式的结果值时,会在运行时抛出异常.
      • dynamic list = new List<int>();
        var result = list.Add (5); // RuntimeBinderException thrown.
      • 而在此种情况下,使用var/object会造成编译时错误.
      • 设计dynamic的表达式,本身也是dynamic的.
      • dynamic x = 2;
        var y = x * 3; // Static type of y is dynamic
      • 编译器会"尽可能早地确定类型"
        • class Program
          {
          static void Foo (object x, object y) { Console.WriteLine ("oo"); }
          static void Foo (object x, string y) { Console.WriteLine ("os"); }
          static void Foo (string x, object y) { Console.WriteLine ("so"); }
          static void Foo (string x, string y) { Console.WriteLine ("ss"); }
          static void Main()
          {
          object o = "hello";
          dynamic d = "goodbye";
          Foo (o, d); // os
          }
          }
      • 不可(Dynamic)调用的Function.
        • 扩展方法(附加类型:扩展方法所定义的类)
        • 转型为接口后,调用接口的方法.
        • 被子类隐藏的父类成员.
        • 动态绑定需要两块信息:方法名,调用方法的对象.
        • 根源:以上3种情况下,都需要附加的类型.而此附加类型只在编译时可知.
  • Dynamic Language Runtime.
    • 在动态/静态语言中,提供运行时服务来统一动态编程.
    • 简化了编写新的动态语言的工作.
      • 编写者不再需要emit IL.而是工作在Expression Tree级别上.
    • call-site caching
      • 在Dynamic绑定时,避免不必要的重复的成员解析的开销.
      • Call-site
        • 充当调用者和被调用者的中介.
      • 对于一个动态操作,编译器最终会将其反映为Expression Tree形式,并被一个call site管理.
      • 之后,在运行时DLR使用表达式树来进行Binding操作.,
      • 使用静态字段来存储call site,来避免每次调用时的重复创建.
    • 统合数字类型
      • static dynamic Mean (dynamic x, dynamic y)
        {
          return (x + y) / 2;
        }
        static void Main()
        {
          int x = 3, y = 5;
          Console.WriteLine (Mean (x, y));
        }
      • 这样会牺牲编译时的类型安全检查.
      • string s = Mean (3, 5); // Runtime error!而会编译通过!.
      • static T Mean<T> (T x, T y)
        {
          dynamic result = ((dynamic) x + y) / 2;
          return (T) result;
        }
      • 解决方法是引入泛型,并在运算内部,将参数显示转换为dynamic.
      • 动态绑定会造成一定的性能损失,如果根据监测结果,某种类型的调用是性能瓶颈,那么使用该特定类型来重载dynamic操作.而编译器会优先使用这些编译时就能确定类型的重载方法版本.
      • static double Mean (double x, double y)
        {
          return (x + y) / 2;
        }
  • 动态成员重载解析
    • 使用dynamic类型参数来调用静态已知方法,会将成员重载解析过程推迟到Runtime.
    • 简化Visitor模式
      • Visitor模式的本质是在不改动寄存Class的前提下,给一个Class继承层次添加方法.
      • 该模式的静态实现方式,精巧且不直观.并且需要被访问类要"Visitor友好"即暴露Visitor方法.
      • class Person
        {
          public string FirstName { get; set; }
          public string LastName { get; set; }
          // The Friends collection may contain Customers & Employees:
          public readonly IList<Person> Friends = new Collection<Person> ();
        }
      • class Customer : Person { public decimal CreditLimit { get; set; } }
        class Employee : Person { public decimal Salary { get; set; } }
      • 需求:将Person及其子类的信息写入XML中.
      • 解决方案1
        • 在Person中定义virtual ToXMLElement().
        • 然后在两个子类中重载它,并写入子类特定属性.
        • 问题:1,这三个类可能不在你控制之下(即无法修改它们).2,这样给这3个类添加了过多的职责.
      • 解决方案2
        • class ToXElementPersonVisitor
          {
          public XElement DynamicVisit (Person p)
          {
          return Visit ((dynamic)p);
          } XElement Visit (Person p)
          {
          return new XElement ("Person",
          new XAttribute ("Type", p.GetType().Name),
          new XElement ("FirstName", p.FirstName),
          new XElement ("LastName", p.LastName),
          p.Friends.Select (f => DynamicVisit (f))
          );
          } XElement Visit (Customer c) // Specialized logic for customers
          {
          XElement xe = Visit ((Person)c); // Call "base" method
          xe.Add (new XElement ("CreditLimit", c.CreditLimit));
          return xe;
          } XElement Visit (Employee e) // Specialized logic for employees
          {
          XElement xe = Visit ((Person)e); // Call "base" method
          xe.Add (new XElement ("Salary", e.Salary));
          return xe;
          }
          }
      • 变种
        • 如果需要有多个Visitor,那么可以定义一个抽象基类.
        • abstract class PersonVisitor<T>
          {
            public T DynamicVisit (Person p) { return Visit ((dynamic)p); }
            protected abstract T Visit (Person p);
            protected virtual T Visit (Customer c) { return Visit ((Person) c); }
            protected virtual T Visit (Employee e) { return Visit ((Person) e); }
          }
        • 这样,子类只需针对不同的Person类型来实现特殊的Visitor方法.
          •     class ToXElementPersonVisitor : PersonVisitor<XElement>
            {
            protected override XElement Visit(Person p)
            {
            return new XElement("Person",
            new XAttribute("Type", p.GetType().Name),
            new XElement("FirstName", p.FirstName),
            new XElement("LastName", p.LastName),
            p.Friends.Select(f => DynamicVisit(f))
            );
            }
            protected override XElement Visit(Customer c)
            {
            XElement xe = base.Visit(c);
            xe.Add(new XElement("CreditLimit", c.CreditLimit));
            return xe;
            }
            protected override XElement Visit(Employee e)
            {
            XElement xe = base.Visit(e);
            xe.Add(new XElement("Salary", e.Salary));
            return xe;
            }
            }
      • Multiple Dispatch
        • virtual方法调用

          • 基于名称和签名,编译器必须在编译时定位到一个特定的重载方法上.
          • 重载解析完全发生在编译时,所以重载方法参数的解析也发生在编译时.
          • 它被称为single dispatch.
          • animal.Walk (owner);
          • 编译器决定调用的依据是animal的类型,即接收者的类型.
          • 而对于owner参数,如果Walk方法针对其继承体系有不同的实现,那么是无视的.
        • dynamic call
          • animal.Walk ((dynamic) owner);
          • 推迟重载解析到运行时.
          • Walk的重载版本的解析,依赖于animal和owner两个对象的具体类型(运行时类型).
          • 称为Multiple Dispatch.除了接收者的类型,方法参数的类型也会参与到重载解析决策中.
      • 匿名调用泛型类型的成员
        • 静态类型一方面保证了编译时的正确性,但又可能使得一些代码难以编写.
        • 此时,除了Reflection,动态绑定也可以对应.
        • public class Foo<T> { public T Value; }
              static void Write (object obj)
              {
                  if (obj is Foo<>) // Illegal
                  Console.WriteLine ((Foo<>) obj).Value); // Illegal
              }
        • 不能调用unbound的泛型类型的方法.
        • 动态调用的方案1
          • static void Write (dynamic obj)
            {
              try { Console.WriteLine (obj.Value); }
              catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException) {...}
            }
          • 问题
            • 混乱和低效,在抛出异常时,不能确定是类型没有value属性还是value值为null.
            • 在以下的情况下,不能达到预期
              • FOO是一个接口.
              • Value被显示实现.
              • 实现了IFoo的类型是不可访问的.
        • 动态调用方案2
          • static void Write (dynamic obj)
            {
              object result = GetFooValue (obj);
              if (result != null) Console.WriteLine (result);
            }
            static T GetFooValue<T> (Foo<T> foo) { return foo.Value; }
            static object GetFooValue (object foo) { return null; }
          • Object参数类型的GetFooValue方法作为其它类型的回退调用.
            • 如果调用时,参数的类型不是基于Foo<T>的,会调用之.
          • 也可以不编写该重载方法,而使用catch来捕获Exception.
    • 实现动态对象
      • 对象可以通过实现IDMOP接口,或者继承DynamicObject来提供绑定语义.
      • Dynamic对象
        • 重载TryInvokeMethod()让用户控制对动态对象的方法调用的执行.
        • 它还含有TryGet/SetMember,TryGet/SetIndex等方法来处理对应的操作.
        • 如果这些Try方法能够成功处理,那么返回true,否则返回false,然后语言Binder会查找对应的方法.如果还是找不到抛出RuntimeBinderException.
      • ExpandoObject
        • 用途:一个词典,以string为key.值是object.
        • 该对象实现了IDictionary<string,object>接口.
        • dynamic x = new ExpandoObject();
          x.FavoriteColor = ConsoleColor.Green;
          x.FavoriteNumber = 7;
          Console.WriteLine (x.FavoriteColor); // Green
          Console.WriteLine (x.FavoriteNumber); // 7
    • 与动态语言交互
      • 虽然C#支持动态绑定,但是不能执行以string描述的表达式

        • string expr = "2 * 3";
          // We can't "execute" expr
        • 原因是将string转换为一个表达式需要语义和词法分析器.而它们被绑定到C#的编译器上,在运行时是不可见的.
        • 在运行时,C#只提供了一个Binder.它的职责是告诉DLR如何解析已经构建好的表达式树.
      • 可以在C#中引入IntroPython之类的DLL,然后进行动态语言的调用.
      • 类型自动地在.Net和Python之间转换,我们可以在Python中直接使用.Net中的类型.
      • // The following string could come from a file or database:
        string auditRule = "taxPaidLastYear / taxPaidThisYear > 2";
        ScriptEngine engine = Python.CreateEngine ();
        ScriptScope scope = engine.CreateScope ();
        scope.SetVariable ("taxPaidLastYear", 20000m);
        scope.SetVariable ("taxPaidThisYear", 8000m);
        ScriptSource source = engine.CreateScriptSourceFromString (
        auditRule, SourceCodeKind.Expression);
        bool auditRequired = (bool) source.Execute (scope);
        Console.WriteLine (auditRequired); // True

Dynamic编程的更多相关文章

  1. [C#]嵌入互操作类型

    嵌入互操作类型(Embed Interop Types) 运用office编程调用Excel 的PIA时Microsoft.Office.Interop.Excel.dll时会产生如下问题: 1.提示 ...

  2. C#编程总结(十四)dynamic

    http://www.cnblogs.com/yank/p/4177619.html C#编程总结(十四)dynamic 介绍 Visual C# 2010 引入了一个新类型 dynamic. 该类型 ...

  3. C#编程总结 dynamic(转)

    介绍 Visual C# 2010 引入了一个新类型 dynamic. 该类型是一种静态类型,但类型为 dynamic 的对象会跳过静态类型检查. 大多数情况下,该对象就像具有类型 object 一样 ...

  4. 动态编程(Dynamic Programming)

    本文素材来自视频,请自备梯子观看:What Is Dynamic Programming and How To Use It Dynamic Programming:动态编程分为如下几步: 将复杂问题 ...

  5. C#高级编程六十九天----DLR简介 .在.NET中使用DLR(转载) 我也来说说Dynamic

    DLR 一.近年来,在TIOBE公司每个月发布的编程语言排行榜中,C#总是能挤进前十名,而在最近十年来,C#总体上呈现上升的趋势.C#能取得这样的成绩,有很多因素,其中它在语言特性上的锐意进取让人印象 ...

  6. Dart编程实例 - Dynamic 关键字

    Dart编程实例 - Dynamic 关键字 void main() { dynamic x = "tom"; print(x); } 本文转自:http://codingdict ...

  7. C#编程(七十)----------dynamic类型

    原文链接 : http://blog.csdn.net/shanyongxu/article/details/47296033 dynamic类型 C#新增了dynamic关键字,正是因为这一个小小的 ...

  8. UWP简单示例(二):快速开始你的3D编程

    准备 IDE:Visual Studio 2015 了解并学习:SharpDx官方GitHub 推荐Demo:SharpDX_D3D12HelloWorld 第一节 世界 世界坐标系是一个特殊的坐标系 ...

  9. Delphi_09_Delphi_Object_Pascal_面向对象编程

    今天这里讨论一下Delphi中的面向对象编程,这里不做过多过细的讨论,主要做提纲挈领的描述,帮助自己抓做重点. 本随笔分为两部分: 一.面向对象编程 二.面向对象编程详细描述 ------------ ...

随机推荐

  1. (转)OpenLayers3基础教程——OL3基本概念

    http://blog.csdn.net/gisshixisheng/article/details/46756275 OpenLayers3基础教程——OL3基本概念 从本节开始,我会陆陆续续的更新 ...

  2. Java中StringTokenizer类的使用

    StringTokenizer是一个用来分隔String的应用类,相当于VB的split函数. 1.构造函数 public StringTokenizer(String str) public Str ...

  3. AOP注解形式 整合memcache

    1.首先自定义注解 :添加缓存 @Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documented@Inheritedp ...

  4. 【转载】Java 集合详解

    转载:https://www.cnblogs.com/ysocean/p/6555373.html 一.集合的由来 通常,我们的程序需要根据程序运行时才知道创建多少个对象.但若非程序运行,程序开发阶段 ...

  5. 【第一课】kaggle初识

    Evernote Export Crowdflower搜索结果相关性 文件和数据描述 train.csv训练数据集包括: id:产品ID查询:使用的搜索词 product_description:完整 ...

  6. 2018 noip 考前临死挣扎

    基础算法 倍增 贪心 分块 二分 三分 数据结构 线段树 对顶堆 数学 质数 约数 同余 组合 矩阵乘法 图论 二分图判定以及最大匹配 字符串 Tire树 KMP 最小表示法 Hash Manache ...

  7. 12.IDEA中自动导资源包

    在idea工程中,当你赋值一个类文件的部分代码,粘贴到另一个文件中时,需要导入原来文件中的包资源, 自动设置如下

  8. 学习EXTJS6(5)基本功能-进度条组件

    Ext.ProgressBar有二种模式:手动和自动:手动:自己控制显示.进度.更新.清除.自动只需要调用Wait方法即可. 配置项:   配置项 类型 说明 renderTo String 指定页面 ...

  9. dubbo-helloword(二)

    项目框架搭建 工程目录创建 entity存放业务实体类 interface存放server接口 concumer是服务消费者(依赖interface和entity) provider是服务提供者(依赖 ...

  10. 洛谷 P1197 BZOJ 1015 [JSOI2008]星球大战 (ZOJ 3261 Connections in Galaxy War)

    这两道题长得差不多,都有分裂集合的操作,都是先将所有操作离线,然后从最后一步开始倒着模拟,这样一来,分裂就变成合并,也就是从打击以后最终的零散状态,一步步合并,回到最开始所有星球都被连为一个整体的状态 ...