Dynamic编程

  • Dynamic Binding
    • 动态绑定

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

1 dynamic d = GetSomeObject();
2 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.

         1 public class Test
         2 {
         3     public dynamic Foo;
         4 }
         5 等同于
         6 public class Test
         7 {
         8     [System.Runtime.CompilerServices.DynamicAttribute]
         9     public object Foo;
        10 }

    • Dynamic转换
      • 动态类型和其它类型之间,会含有隐式转换.
      • 转换成功的前提,是动态对象必须能够与目标对象类型是兼容的.
      • int i = 7;
        dynamic d = i;
        long j = d; // No cast required (implicit conversion)
        
        int i = 7;
        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编程

时间: 2024-11-05 21:46:14

Dynamic编程的相关文章

C#编程总结(十四)dynamic

C#编程总结(十四)dynamic 介绍 Visual C# 2010 引入了一个新类型 dynamic. 该类型是一种静态类型,但类型为 dynamic 的对象会跳过静态类型检查. 大多数情况下,该对象就像具有类型 object 一样. 在编译时,将假定类型化为 dynamic 的元素支持任何操作. 因此,您不必考虑对象是从 COM API.从动态语言(例如 IronPython).从 HTML 文档对象模型 (DOM).从反射还是从程序中的其他位置获取自己的值. 但是,如果代码无效,则在运行

C#编程总结 dynamic(转)

介绍 Visual C# 2010 引入了一个新类型 dynamic. 该类型是一种静态类型,但类型为 dynamic 的对象会跳过静态类型检查. 大多数情况下,该对象就像具有类型 object 一样. 在编译时,将假定类型化为 dynamic 的元素支持任何操作. 因此,您不必考虑对象是从 COM API.从动态语言(例如 IronPython).从 HTML 文档对象模型 (DOM).从反射还是从程序中的其他位置获取自己的值. 但是,如果代码无效,则在运行时会捕获到错误. var与dynam

C#高级编程七十天----dynamic类型

dynamic类型 C#新增了dynamic关键字,正是因为这一个小小的关键字,C#像前迈进了一大步. dynamic是一个类型关键,声明为dynamic的类型与"静态类型"(这里的静态是指编译时确定的类型,例如int,double类型)相比最大的特定它是"动态类型",它会在运行时尝试调用方法,这些方法的存在与否不是编译时期检查的,而是在运行时查找,如果方法存在并且参数正确,会正确调用,否则会抛出异常. 案例: dynamic d = Console.Out; dy

编程内功修炼 - 算法

编程内功讲什么? 主要讲解以下算法: 分治法 堆排序 二叉树 动态规划 贪心算法 图 算法的作用: 算法解决了哪些问题? 互联网信息的访问检测,海量数据的管理 在一个交通图中,寻找最近的路 人类基因工程,dna有10万个基因,处理这些基因序列需要复杂的算法支持 上面的算法是我们没有接触到,或者是封装到底层的东西,那么作为程序员,在日常编码过程中会在什么地方使用算法呢? 在你利用代码去编写程序,去解决问题的时候,其实这些编码过程都可以总结成一个算法,只是有些算法看起来比较普遍比较一般,偶尔我们也会

GPU渲染管线与可编程着色器

本文由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://blog.csdn.net/poem_qianmo/article/details/71978861 这篇文章是解析计算机图形学界"九阴真经总纲"一般存在的<Real-Time Rendering 3rd>系列文章的第三篇.将带来RTR3第三章内容"Chapter 3 The Graphics Processing Unit 图形处理器"的总结.概括与提炼. 这章的主要内容是介绍G

370 门免费编程与计算机科学在线课程

简评:这篇文章为大家整理出 370 门精选的免费高质量编程计算机科学类的课程(涵盖程序语言.人工智能.深度学习与机器学习等热门话题).这370 门课程是从 Class Central数据库里面的 7000 门课程挑选出来的,每个课程的 Rating(评价)也是由该网站上获取下来的平均值. 370 门课程里面根据难易程度被分为: 入门 中级 进阶 所收录的大部分教程都已经更新完毕了,你可以按照自己的节奏(Self Paced)随时观看学习,有小部分教程还在持续更新至中,当然了,它们全都是免费的!

Dynamic dispatch

Dynamic dispatch动态调度.动态分发 In computer science, dynamic dispatch is the process of selecting which implementation of a polymorphic operation (method or function) to call at run time. It is commonly employed in, and considered a prime characteristic of

Win32编程API 基础篇 -- 1.入门指南 根据英文教程翻译

入门指南 本教程是关于什么的 本教程的目的是向你介绍使用win32 API编写程序的基础知识(和通用的写法).使用的语言是C,但大多数C++编译器也能成功编译,事实上,教程中的绝大多数内容都适用于任何可以连接API的语言,包括Java.Assembly和Visual Basic:我不会向你呈现任何跟这些语言相关的代码,这需要你在本教程的指导下自己去完成,有一些人在本API的基础上使用其他语言进行编程取得了相当的成功. 本教程不会教你C语言,也不会告诉你怎样去运行你特定的编译器(Borland C

如何让C#像JavaScript一样编程

JavaScript是一门动态语言,可以动态的给对象添加属性和方法,非常方便.那么有没有一种方式可以让C#也具备动态添加属性和方法的能力,像Javascript一样进行编程? 下面就介绍一个很不错的框架ClaySharp可以实现上述功能. 下面的代码就是用ClaySharp构建一个New对象后,可以用New来动态添加属性: 下面详细说一下步骤: 1 新建一个控制台程序,并引入类库 必须要引入ClaySharp命名空间: 1 //ClaySharp可以让C#具备JavaScript的动态编程能力