.NET 4.0 FAQ 第一部分—DLR
简介
本篇文章中我们将讨论.NET FrameWork 4.0提供了哪些新特性。然后再探讨DLR特性中的动态对象和Expando对象。我们也将会创建一个Expando对象来看我们可以从中获得哪些益处。很多的开发人员误以为动态对象是用来替代反射和object类型的,我们也将会纠正这种错误的概念。
.NET 4.0中有哪些重要的新特性?
与其去浏览.NET 4.0冗长的新特性列表,我们还是专注于我们认为最重要的三项新特性上吧。
•WF和WCF 4.0:这是.NET 4.0中的一个重大改变。WCF中引入了简化的配置、发现和路由服务。WF的核心编程模型也得到了改变,变得更加简易和健壮。最重大事件的就是WCF和WF的整合。
•动态语言运行时:DLR给.NET 4.0的CLR赋予了动态编程的能力。我们将会在文章接下来的部分探讨这一点。
•并行扩展:这有助于多核系统中的并行计算。.NET 4.0的LinQ中添加了PLinQ来支持并行执行。.NET 4.0引入了TPL(Task parallel library),其中暴露了像并行的“For”和“ForEach”等并行构造,它们可以调用普通方法或者委托。
接下来我们将详细讲述以上提到新特性。
DLR在.NET 4.0中扮演何种角色呢?
DLR(Dynamic language runtime)是一组服务,它给CLR赋予了动态编程的能力。DLR使得像LISP、JavaScript、PHP、Ruby一样的动态语言可以运行在.NET FrameWork上。
编程语言分为动态类型语言和静态类型语言。在静态类型语言中,你需要在编译时或者说是设计时指明对象的类型。而动态类型语言可以在运行时识别对象的类型。DLR使得CLR能够作为动态语言代码运行的宿主环境。
有了DLR,Ruby、Python、JavaScript等动态语言就可以无缝的运行在CLR之上了。DLR还有助于为你最喜欢的动态语言来构建最佳体验。这样,你的与动态语言交互的代码就会变得更加简洁。
DLR并不仅限于动态语言。你也可以通过使用COM Interop Binder来以一种更加简洁的方式去调用MS Office组件。
DLR的一大优势就是它为动态语言提供了一个子系统。
可以讲讲DLR子系统的细节吗?
DLR有三个基本子系统:
•表达式树:我们可以用它来以AST(抽象语法树)的方式表达语义。DLR会动态生成使用AST的代码,这些代码可以在CLR之上执行。JavaScript和Ruby等动态语言可以运行在CLR之上很大程度上得是益于表达式树的助力。
•调用栈缓存:当你调用动态对象的方法时,DLR会把那些方法调用的信息都缓存起来。在后续的调用中,DLR就会使用这些缓存起来的信息来完成快速调度。
•动态对象互操作性(DOI):DOI系统中包含一组用来创建动态对象的类。开发人员可以使用这些类来创建可以在动态语言或者静态语言中使用的动态对象。
接下来我们会对上述的新特性做详细讲解。
我们该如何使用动态语言的对象,又该如何向动态语言暴露一个类呢?
要使用一个DLR支持的语言创建的对象,我们可以使用dynamic关键字。而要向动态语言暴露一个类,我们可以使用Expando类。
所以如果你想要使用一个诸如由Python、Ruby或者JavaScript等动态语言创建的对象的话,你就可以使用dynamic的引用来指向该对象。如果你想要让你的类可以被动态语言使用,你的类就需要继承自Expando类。我们稍后就会看到这两种例子。
(上图中文字:Dynamic对象有助于使用动态语言创建的对象。Expando对象有助于向动态语言暴露一个类。)
有什么Dynamic对象的例子吗?
我们已经讲过Dynamic对象有助于去访问由受DLR支持的动态语言创建的对象。dynamic关键字是DOI子系统的一部分。
如果你把一个对象赋值给一个dynamic类型的变量的话(dynamic x = new SomeClass()),所有对x的方法的调用,对x的属性的访问,以及对x的运算符操作都会被延迟到运行时去,而且编译器在编译时不会对x做任何的类型检查。
考虑下面的代码片段,我们在其中试着去调用excel的方法。
// Get the running object of the excel application object objApp = System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application"); // Invoke the member dynamically object x = objApp.GetType().InvokeMember("Name", System.Reflection.BindingFlags.GetProperty, null, objApp, null); // Finally get the value by type casting MessageBox.Show(x.ToString());
使用dynamic关键字来完成相同功能的代码。
// Get the object using dynamic objApp1 = System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application"); // Call the MessageBox.Show(objApp1.Name);
你可以很清楚地发现访问属性的语法的简化。InvokeMember这个方法含义模糊而且容易出错。使用dynamic关键字,我们可以看到代码得到了简化。
如果你试着在Visual Studio中查看x的属性,你将会得到提示说此表达式的值只有在运行时才可以被断定。
Dynamic、Object和反射这三者之间有何关系?
很多的开发人员认为引入Dynamic的目的就是要替换掉反射或者Object这种类型。其实dynamic的主要目的是在静态类型的语言中去无缝的访问动态语言创建的对象。也正是由于这一点,它的部分目的与反射和object类型重合了。
由于dynamic可以简化代码并具有缓存的优势,它最终会替换掉反射和object类型。但是引入dynamic的初衷绝对不是要去代替反射和object类型,只是它们的目的重合了而已。
Dynamic |
Object / 反射 |
Dynamic对象是DLR引擎提供的一个小特性,我们可以通过使用它来访问动态语言创建的对象。而DLR不仅使得动态语言创建的对象可以被访问,它还使得你的类可以被暴露给动态语言。 |
反射和object类型只是用来引用未知类型。反射和object类型无法帮你把你的类暴露给其他语言。它们只是用来访问那些直到运行时才得知具体类型的对象的。 |
语法相当简单。 |
语法有点难。 |
因为有了方法访问的缓存,性能得以提升。 |
目前还没有对访问方法的缓存。 |
dynamic关键字有什么优缺点?
差的程序员即使使用最好的语言也会写出很差的代码,而好的程序员即使使用最差的编程语言也可以健步如飞。Dynamic关键字是一个减少复杂性的好工具,但是如果使用不当的话,它就是个诅咒。
Dynamic关键字的优势有:
•帮你与动态语言相交互。
•消除混乱的反射代码并可以简化代码复杂度。
•有方法访问的缓存,可以提升性能。
缺点:
•当与强类型的类一起使用的时候可能会损害性能。
Expando对象是什么呢?
Expando对象是为交互性的另一端服务的,比如说它使得你的自定义类型可以在动态语言中被访问。所以你可以创建一个Expando类的实例并把它传递给像Ruby、JavaScript、Python之类的动态语言。Expando对象有助于在运行时添加属性。它是动态属性包的一种高效实现。要想使用Expando对象,我们首先要引入System.Dynamic命名空间。然后创建一个ExpandoObject的对象并把它赋值给一个用dynamic声明的引用。
dynamic obj = new ExpandoObject();
要创建动态属性,我们只需要写出属性名并给它赋值。
obj.Customername = "Some Customer Name";
最后我们显示该值。
MessageBox.Show(obj.Customername);
我们可以实现自己的Expando对象吗?
Expando对象在内部只是向一个集合中添加属性。所以你可以创建自己的Expando对象。
我们首先需要继承DynamicObject类。
public class clsMyExpando : DynamicObject
{
}
如前所述,我们需要定义一个集合来存储属性。所以第二步我们就要创建一个字典对象来维护集合中的属性。
public class clsMyExpando : DynamicObject { Dictionary items= new Dictionary(); }
我们现在可以使用TryGetMember和SetGetMember来定义我们的属性访问器。
public class clsMyExpando : DynamicObject { Dictionary items = new Dictionary(); public override bool TryGetMember(GetMemberBinder binder, out object result) { return items.TryGetValue(binder.Name, out result); } public override bool TrySetMember(SetMemberBinder binder, object value) { items[binder.Name] = value; return true; }}
现在我们可以创建我们自己的Expando类的对象并把它赋值给dynamic类的引用。下面的代码片断中我们给一个叫做Name的动态属性赋了值。
dynamic obj = new clsMyExpando();
obj.Name = "Dynamic Property";
使用自定义Expando对象有什么益处呢?
使用Expando对象可以提升性能。如果你的类有静态属性也有动态属性的话,你可以像如下代码一样的在自定义Expando类中创建静态属性。当该对象的静态属性被访问的时候,它不会去调用字典的成员方法,这样也就增进了性能。DLR引擎会首先试着去访问属性名而不是去调用TryGetMember和SetGetMember。
如果你不需要动态属性也不需要和动态语言交互的话,就要避免使用自定义Expando类型。而如果你需要使用动态属性的话,一定要确保你是继承了DynamicObject类的。
public class clsMyExpando : DynamicObject { Dictionary items = new Dictionary(); private string _Name; public string Name { get { return _Name; } set { _Name = value; } } public override bool TryGetMember( GetMemberBinder binder, out object result) { return items.TryGetValue(binder.Name, out result); } public override bool TrySetMember( SetMemberBinder binder, object value) { items[binder.Name] = value; return true; } }
IDynamicMetaObjectProvider和DynamicMetaObject是什么呢?
动态对象实现IDynamicMetaObjectProvider并返回DynamicMetaObject。这两个类型都是实现动态语言之间交互性的核心部分。