Dynamic编程
- 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;
}
- Object和Dynamic有很深的相等性
- 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;
}
- static dynamic Mean (dynamic x, dynamic y)
- 动态成员重载解析
- 使用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.除了接收者的类型,方法参数的类型也会参与到重载解析决策中.
- virtual方法调用
- 匿名调用泛型类型的成员
- 静态类型一方面保证了编译时的正确性,但又可能使得一些代码难以编写.
- 此时,除了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的类型是不可访问的.
- static void Write (dynamic obj)
- 动态调用方案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.
- static void Write (dynamic obj)
- 实现动态对象
- 对象可以通过实现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如何解析已经构建好的表达式树.
- string expr = "2 * 3";
- 可以在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
- 虽然C#支持动态绑定,但是不能执行以string描述的表达式
Dynamic编程的更多相关文章
- [C#]嵌入互操作类型
嵌入互操作类型(Embed Interop Types) 运用office编程调用Excel 的PIA时Microsoft.Office.Interop.Excel.dll时会产生如下问题: 1.提示 ...
- C#编程总结(十四)dynamic
http://www.cnblogs.com/yank/p/4177619.html C#编程总结(十四)dynamic 介绍 Visual C# 2010 引入了一个新类型 dynamic. 该类型 ...
- C#编程总结 dynamic(转)
介绍 Visual C# 2010 引入了一个新类型 dynamic. 该类型是一种静态类型,但类型为 dynamic 的对象会跳过静态类型检查. 大多数情况下,该对象就像具有类型 object 一样 ...
- 动态编程(Dynamic Programming)
本文素材来自视频,请自备梯子观看:What Is Dynamic Programming and How To Use It Dynamic Programming:动态编程分为如下几步: 将复杂问题 ...
- C#高级编程六十九天----DLR简介 .在.NET中使用DLR(转载) 我也来说说Dynamic
DLR 一.近年来,在TIOBE公司每个月发布的编程语言排行榜中,C#总是能挤进前十名,而在最近十年来,C#总体上呈现上升的趋势.C#能取得这样的成绩,有很多因素,其中它在语言特性上的锐意进取让人印象 ...
- Dart编程实例 - Dynamic 关键字
Dart编程实例 - Dynamic 关键字 void main() { dynamic x = "tom"; print(x); } 本文转自:http://codingdict ...
- C#编程(七十)----------dynamic类型
原文链接 : http://blog.csdn.net/shanyongxu/article/details/47296033 dynamic类型 C#新增了dynamic关键字,正是因为这一个小小的 ...
- UWP简单示例(二):快速开始你的3D编程
准备 IDE:Visual Studio 2015 了解并学习:SharpDx官方GitHub 推荐Demo:SharpDX_D3D12HelloWorld 第一节 世界 世界坐标系是一个特殊的坐标系 ...
- Delphi_09_Delphi_Object_Pascal_面向对象编程
今天这里讨论一下Delphi中的面向对象编程,这里不做过多过细的讨论,主要做提纲挈领的描述,帮助自己抓做重点. 本随笔分为两部分: 一.面向对象编程 二.面向对象编程详细描述 ------------ ...
随机推荐
- MVC控制器返回值
public ActionResult Index(string id)//主页 //参数string searchString 访问方式为index?searchString=xxxx .参数str ...
- Python基础学习(day1)
一.Python几点使用规范: 1.关于引号的使用规范 (1)字符串中含有单引号,则使用双引号外扩 print("It's ok") (2)字符串中含有双引号,则使用单引号外扩 p ...
- yum的方式搭建mysql
1.安装相应的软件yum install mysql : 安装mysql客户端 yum install mysql-server 安装服务端 yum install mysql-devel 安装相关的 ...
- 【剑指Offer】15、反转链表
题目描述: 输入一个链表,反转链表后,输出新链表的表头. 解题思路: 本题比较简单,有两种方法可以实现:(1)三指针.使用三个指针,分别指向当前遍历到的结点.它的前一个结点以及后一个结 ...
- SSL/TLS 加密新纪元 - Let's Encrypt
转自: https://linux.cn/article-6565-1.html SSL/TLS 加密新纪元 - Let's Encrypt 根据 Let's Encrypt 官方博客消息,Let's ...
- PAT 1065. A+B and C
Given three integers A, B and C in [-263, 263], you are supposed to tell whether A+B > C. Input S ...
- Codeforces 947E Perpetual Subtraction (线性代数、矩阵对角化、DP)
手动博客搬家: 本文发表于20181212 09:37:21, 原地址https://blog.csdn.net/suncongbo/article/details/84962727 呜啊怎么又是数学 ...
- 【Codeforces 933A】A Twisty Movement
[链接] 我是链接,点我呀:) [题意] [题解] 因为只有1和2. 所以最后肯定是若干个1接着若干个2的情况. 即11...11222...222这样的. 1.首先考虑没有翻转的情况. 那么就直接枚 ...
- 格式化LInux后开机进入grub怎么办
问题:格式化Linux系统盘之后,重启进入grub 1.grub 引导进入windows系统 进入grub grub>rootnoverify (hd0,1) [可以使用Tab键( 比如 roo ...
- JavaScript向window onload添加加载函数
有时候我们需要在页面加载事件后完成一些函数操作,对于函数比较多的情况下可以写一个统一的加载函数 .本函数来自于JavaScript DOM 编程艺术: function addLoadEvent(fu ...