微软锋利的刀C#40新特性之动态编程.doc_第1页
微软锋利的刀C#40新特性之动态编程.doc_第2页
微软锋利的刀C#40新特性之动态编程.doc_第3页
微软锋利的刀C#40新特性之动态编程.doc_第4页
微软锋利的刀C#40新特性之动态编程.doc_第5页
全文预览已结束

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

微软锋利的刀 C#40新特性之动态编程.txt爱一个人很难,恨一个人更难,又爱又恨的人最难。爱情永远不可能是天平,想在爱情里幸福就要舍得伤心!有些烦恼是我们凭空虚构的,而我们却把它当成真实去承受。 本文由dhloveyzk贡献 doc文档可能在WAP端浏览体验不佳。建议您优先选择TXT,或下载源文件到本机查看。 近几年来,在 TIOBE 公司每个月发布的编程语言排行榜1中,C#总是能挤 进前 10 名,而在近 10 年的编程语言排行榜中,C#总体上呈现上升的趋势。C# 能取得这样的成绩,有很多因素在起作用,其中,它在语言特性上的锐意进取让 人印象深刻(图 1)。 2010 年发布的 C# 4,最大的创新点是拥有了动态编程语言的特性。 1 动态编程语言的中兴 动态编程语言并非什么新鲜事物,早在面向对象编程语言成为主流之前,人 们就已经使用动态编程语言来开发了。即使在 Java、C#、C+等面向对象编程语 言繁荣兴旺、大行于世的年代,动态编程语言也在“悄悄”地攻城掠地,占据了 相当的开发领域,比如 JavaScript 业已成为 Web 客户端事实上的主流语言。 最近这几年,动态编程语言变得日益流行,比如 Python、Ruby 都非常活跃, 使用者众多。 这里有一个问题, 为什么我们需要在开发中应用动态编程语言?与 C#和 Java 这类已经非常成熟且功能强大的静态类型编程语言相比,动态编程语言有何优 势? 简单地说,使用动态编程语言开发拥有以下的特性: (1)支持 REPL(Read-evaluate-print Loop:“读入执行输出”循环迭代) 的开发模式,整个过程简洁明了,直指问题的核心。 举个简单的例子, 2 所示为使用 IronPython2编程计算 图 “1+2+100” 的屏幕截图,我们可以快速地输入一段完成累加求和的代码,然后马上就可以看 到结果: 图 2 使用 IronPython 编程 如果使用 C#开发就麻烦多了,您得先用 Visual Studio 创建一个项目,然 后向其中添加一个类,在类中写一个方法完成求和的功能,再编写调用这一方法 的代码,编译、排错,最后才能得到所需的结果 很明显,对于那些短小的工作任务而言,动态编程语言所具备的这种 REPL 开发模式具有很大的吸引力。 (2)扩展方便。用户可以随时对代码进行调整,需要什么功能直接往动态对 象上“加”就是了,不要时又可以移除它们。而且这种修改可以马上生效,并不 需要像 C#那样必须先修改类型的定义和声明,编译之后新方法才可用。 换句话说:使用动态语言编程,不需要“重量级”的 OOAD,整个开发过程 迭代迅速而从不拖泥带水。 (3)动态编程语言的类型解析是在运行时完成的,可以省去许多不必要的类 型转换代码,因此,与静态编程语相比,动态编程语言写的代码往往更紧凑,量 更少。 动态编程语言主要的弱点有两个: (1)代码中的许多错误要等到运行时才能发现,而且需要特定的运行环境支 持,对其进行测试不太方便,也不支持许多用于提升代码质量的各种软件工程工 具,因此不太适合于开发规模较大的、包容复杂处理逻辑的应用系统。 (2)与静态编程语言相比,动态编程语言编写的程序性能较低。不过随着计 算机软硬件技术的不断进步,比如多核 CPU 的广泛应用,动态编程语言引擎和运 行环境不断地优化,动态编程语言编写的程序性能在不断地提升,在特定的应用 场景下,甚至可以逼近静态语言编写的程序。 拥抱“动态编程” 2 拥抱“动态编程”特性的 C# 4 为了让 C#、 Visual Basic 等.NET 编程语言能具备动态编程语言的特性, .NET 4.0 引入了一个“DLR(Dynamic Language Runtime:动态语言运行时)”(图 3)。 DLR 运行于 CLR 之上,提供了一个动态语言的运行环境,从而允许 Python、 Ruby 等动态语言编写的程序在.NET 平台上运行,同时,现有的.NET 静态类型编 程语言,比如 C#和 Visual Basic,也可以利用 DLR 而拥有一些动态编程语言的 特性。 (1)使用 (1)使用 C# 4 编写动态的代码 C# 4 新增了一个 dynamic 关键字,可以用它来编写“动态”的代码。 例如, 以下代码创建了一个 ExpandoObject 对象(注意必须定义为 dynamic): dynamic dynamicObj = new ExpandoObject(); 这一对象的奇特之处在于,我们可以随时给它增加新成员: dynamicObj.Value = 100; /添加字段 dynamicObj.Increment = new Action() = dynamicObj.Value+); /添 加方法 这些动态添加的成员与普通的类成员用法一样: for (int i = 0; i 10; i+) dynamicObj.Increment();/调用方法 Console.WriteLine(dynamicObj.Value=0,dynamicObj.Value);/访问 字段 ExpandoObject 对象实现了 IDictionary 接口,可看成是一个字典对象,所 有动态添加的成员都是这个字典对象中的元素,这意味我们不仅可以添加新成 员,还可以随时移除不再需要的成员: /移除 Increment 方法 (dynamicObj as IDictionary).Remove(Increment); 方法移除之后,再尝试访问此方法将引发 RuntimeBinderException 异常。 (2) (2)使用 dynamic 关键字简化与 COM 组件交互的代码 要在.NET 这个“托管世界”里调用“非托管世界”中的 COM 组件,我们必 须通过 “互操作程序集(Interop Assembly)”作为桥梁,“互操作程序集”定 义了 CLR 类型与 COM 类型之间的对应关系。 只要给.NET 项目添加对“互操作程序集”的引用,就可以在.NET 应用程序 中创建这一程序集所包容的各种类型的实例(即 COM 包装器对象), 对这些对象的 方法调用(或对其属性的存取)将会被转发给 COM 组件。 以调用 Word 为例,在 C# 4.0 之前您可能经常需要编写这样的代码: Object wordapp = new Word.Application(); /创建 Word 对象 Object fileName = “MyDoc.docx” ;/指定 Word 文档 Object argu = System.Reflection.Missing.Value; Word.Document doc = wordapp.Documents.Open(ref fileName, ref argu, ref argu, ref argu, ref argu, ref argu, ref argu, ref argu, ref argu, ref argu, ref argu, ref argu, ref argu, ref argu, ref argu, ref argu); 上述对 Open()方法的调用语句只能用“恐怖”一词来形容,其原因是 Word 组件中的 Open()方法定义了太多的参数。 C#4 使用 dynamic 关键字,配合从 Visual Basic 中学来的“命名参数与可 选参数”这两个新语法特性,可以写出更简洁的代码: dynamic wordapp = new Word.Application(); dynamic doc = wordapp.Documents.Open(FileName: “MyDoc.docx”); 上述代码中省去了用不着的参数,并且可以去掉参数前的 ref 关键字。 当上述代码运行时,DLR 会使用反射技术将 dynamic 表达式“绑定(bind)” 到 COM 互操作程序集中所包容的 Word.Application 代理对象。 (3)C# 4 动态编程技术内幕 C#4 中所定义的 dynamic 变量可以引用以下类型的对象: l 传统的“静态”的 CLR 对象。 l COM 包装器对象。前面已经介绍了这方面的内容。 l 实现了 IDynamicMetaObjectProvider 接口的 “动态对象” ExpandoObject , 就是这种类型对象的实例。 l 基于 DLR 实现的动态语言(比如 IronRuby 和 IronPython)所创建的对象。 从 C#程序员角度来看,所有这四种对象都是一样的,都可用一个 dynamic 变量引用之,而 DLR 在程序运行时动态地将方法调用和字段存取请求“绑定”到 真正的对象上。 dynamic 的功能是由 DLR 所支撑的,是 C#编译器与 DLR 分工合作的成果。 请看以下示例代码: dynamic d = 100; d+; C#编译器在处理上述代码时,它并不去检查变量 d 是否可以支持自增操作, 而是为其创建了一个 CallSite 对象(p_Site1): private static class o_SiteContainer0 public static CallSiteFunc p_S ite1; 中文 MSDN 将 CallSite 译为“动态(调用)站点”,它是 DLR 中的核心组件之 一。 动态站点对象通过 CallSite.Create()方法创建, C#编译器会为其指定一 个派生自 CallSiteBinder 的对象(称为“动态站点绑定对象”)作为其参数。 动态站点绑定对象是与具体语言相关的,比如 IronPython 和 C#都有各自的 动态站点绑定对象。 动态站点绑定对象的主要工作是将代码中的动态表达式(本例中为 d+)转 换为一棵“抽象语法树(AST:Abstract Syntax Tree)”,这棵语法树被称为“DLR Tree”,是在.NET 3.5 所引入的 LINQ 表达式树的基础上扩充而来的,因此,有 时又称其为“表达式树(Expression Tree)” DLR 在内部调用此表达式树的 Compile()方法生成 IL 指令, 得到一个可以被 CLR 所执行的委托(在本例中其类型就是 Func)。 动态调用站点对象(本例中为p_Site1)有一个 Target 属性,它负责引用 这一生成好的委托。 委托生成之后,动态表达式的执行就体现为委托的执行,其实参由 C#编译 器直接“写死”在 IL 代码中。 简化的代码示意如下(通过 Reflector 得到,为便于阅读,修改了变量名): object d = 100; object CS$0$0000 = d; if (p_Site1 = null) p_Site1 = CallSiteFunc.Create (); d = p_Site1.Target(p_Site1, CS$0$0000); 上述类型推断、方法绑定及 IL 代码生成的工作都是在程序运行时完成的。 (4)动态代码很慢吗? (4)动态代码很慢吗? 动态代码很慢吗 动态编程语言易学易用, 代码紧凑, 开发灵活, 但性能则一直是它的 “软肋” 。 为了提升性能,DLR 设计了一个三级缓存策略。 动态站点绑定对象会为动态调用表达式转换而成的语法树加上相应的测试 条件(称为“test”),构成一个“规则(Rule)”,这个规则可以用于判断某个语 法树是否可用于特定的动态调用表达式。 举个例子,请看以下这个动态表达式: d1 + d2 如果在程序运行时 d1 和 d2 都是 int 类型的整数,则 DLR 生成的规则为: if( d1 is int & d2 is int) /测试条件 return (int)d1+(int)d2; /语法树 DLR 通过检查规则中的“测试条件”,就可以知道某个动态表达式是否可以 使用此规则所包容的语法树。 “规则”是 DLR 缓存的主要对象。 前面介绍过的动态站点对象 Target 属性所引用的委托是第一级缓存,它实 现的处理逻辑是这样的: /当前处理规则,属于第 1 级缓存 if( d1 is int & d2 is int) /测试条件 return (int)d1+(int)d2; /满足测试条件,直接返回一个表达式树 /未命中,则在第 2 级、第 3 级缓存中查找,如果找到了,用找到的结果更 新第 1 级缓存 return site.Update(site,d1,d2); 如果 3 级缓存中都没有命中的规则, 则此动态站点所关联的调用站点绑定对 象会尝试创建一个新的规则。 如果创建新规则失败, 则由当前编程语言(比如 C#) 所提供的默认调用站点绑定对象决定如何处理,通常的作法是抛出一个异常。 当前版本的 DLR 第 2 级缓存了 10 条规则,第 3 级则缓存了 100 条规则。 由于 DLR 自身设计了一个“规则”缓存系统,又充分利用了 CLR 所提供的 JIT 缓存(因为所有动态调用代码最终都会转换为 CLR 可以执行的 IL 指令, CLR 而 可以缓存这些代码),使得动态代码仅仅在第一次执行时性能较差,后续的连续 调用其性能可以逼近静态代码。 3 C# 4 与动态语言的集成 由于几乎所有的编程语言都可以使用抽象语法树来表达,因此,在理论上 DLR 支持无限多种编程语言间的互操作,在当前版本中,可以实现 C#/Visual Basic 与 IronPython 和 IronRuby 的互操作,相信很快会出现其他动态编程语言 的 DLR 实现。 一个有趣的地方是当前基于 DLR 实现的动态编程语言都以“Iron”开头,比 如 IronRuby 和 IronPython。 IronPython 的设计者、 的架构设计师 Jim Hugunin DLR 曾经在微软 PDC 2008 大会上解释说主要是为了避免起一个“Python.NET”或 “Python for .NET”之类“微软味十足”的名字,才有了“IronPython”。他 强调:“Iron”系列动态语言将严格遵循动态语言自身的标准和规范,尊重这些 动态语言已有的历史和积累,不会引入一些仅限于.NET 平台的新语言特性,并 且这些语言的.NET 实现保持开源。与此同时,Jim Hugunin 指出 “Iron”系列 语言能很好地与.NET 现有类库、编程语言和工具集成,并且能“嵌入”到.NET 宿主程序中。 (1)动态对象通讯协议 (1)动态对象通讯协议 由于各种动态编程语言之间的特性相差极大, 实现各语言间的互操作是个难 题。为此 DLR 采取了一个聪明的策略,它不去尝试设计一个“通用的类型系统” (CLR 就是这么干的),而是设计了一个“通用的对象通讯协议”,规定所有需要 互操作的动态对象必须实现 IDynamicMetaObjectProvider 接口,此接口定义了 一个 GetMetaObject()方法, 接收一个语法树对象作为参数, 向外界返回一个 “动 态元数据(DynamicMetaObject)”对象: DynamicMetaObject GetMetaObject(Expression parameter); DynamicMetaObject 对象向外界提供了两个重要属性: Restrictions 引用一 组测试条件,Expression 属性则引用一个语法树。这两个属性组合起来就是可 供动态站点对象缓存的“规则(Rule)”。 DLR 中的 “动态站点绑定对象(CallSiteBinder)” 获取了 DynamicMetaObject 对象之后,它调用此对象所提供的各个方法创建“规则”,让“动态站点对象 (CallSite)”的 Target 属性引用它,完成动态绑定的工作。 (2)动态语言集成环境 (2)动态语言集成环境 动态语言集 为了方便地实现静态编程语言与各种动态编程语言间的相互集成,DLR 提供 了一整套称为“通用寄宿(Common Hosting)”的组件,其中包容 ScriptRuntime、 ScriptScope 等类型。 下面我们以 IronPython 为例,介绍如何在 C# 4 开发的程序中集成动态编程 语言代码。 首先需要创建一个 ScriptRuntime 对象,它是一个最顶层的对象,用于在一 个.NET 应用程序域中“嵌入”一个特定动态语言的运行环境: ScriptRuntime pythonRuntime = Python.CreateRuntime(); 接着需要创建一个 ScriptEngine 对象,它是动态语言代码的执行引擎: ScriptEngine engine = pythonRuntime.GetEngine(py); ScriptScope 对象类似于 C#中的命名空间, 其中可以通过定义一些变量向动 态代码传入数据,比如下述代码将一个 C# 创建的 ExpandoObject 对象传给 Python 代码: ScriptScope scope = pythonRuntime.CreateScope(); /C#创建动态对象 dynamic expando = new ExpandoObject(); expando.Name = JinXuLiang; /动态添加一个字段 /让 IronPython 接收 C#创建的 Expando 对象 scope.SetVariable(ExpandoObject, expando); string pythonCode = print ExpandoObject.Name; /IronPython 引擎执行 Python 语句 engine.CreateScriptSourceFromString(pythonCode).Execute(scop

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论