Dynamic关键字和DLR是C#4和.NET Framework 4中重大的新增功能!
一般而言,动态语言不执行编译时类型检查,仅在运行时识别对象的类型。缺少编译时类型检查也会导致 IntelliSense 功能无效。
C# 最初是作为纯静态语言创建的,但 C# 4 添加了一些动态元素,用以改进与动态语言和框架之间的互操作性。C# 团队考虑了多种设计选项,但最终确定添加一个新关键字来支持这些功能:dynamic。
当我们使用 dynamic 关键字时,我们就告诉了编译器关闭编译时检查。网上以及 MSDN 文档中有大量关于如何使用该关键字的示例。
关键字 object 表示 System.Object 类型,它是 C# 类层次结构中的根类型。此关键字经常在编译时无法确定对象类型时使用,而这种情况经常在各种互操作性情形中发生。
从 C# 3.0 起,关键字 var 开始用于隐式类型化局部变量以及匿名类型。此关键字经常与 LINQ 结合使用。当使用 var 关键字声明变量时,将在编译时根据初始化字符串推断该变量的类型。在运行时无法更改该变量的类型。如果编译器不能推断类型,它会生成一个编译错误。
C# 4 中引入的 dynamic 关键字可使某些传统上依赖于 object 关键字的情形更容易编写和维护。实际上,动态类型在后台使用 System.Object 类型。但与 object 不同的是,动态类型不需要在编译时执行显式转换操作,因为它仅在运行时识别类型。
基本的用法,用代码来说吧:
using System; using System.Reflection; namespace csdlr { public class Employee { public string FirstName { get; set; } public void Speak() { Console.WriteLine("My name is {0}", FirstName); } } class Program { static void Main(string[] args) { ////Error 1: //var o_1 = GetASpeaker(); //o_1.Speak(); ////Error 2: //object o_2 = GetASpeaker(); //o_2.Speak(); //1.普通 Employee o = GetASpeaker() as Employee;//var o=GetASpeaker();//OK also. o.Speak(); //2.反射 object o1 = GetASpeaker(); o1.GetType().GetMethod("Speak").Invoke(o, null); //3.Dynamic dynamic o2 = GetASpeaker(); o2.Speak(); Console.ReadKey(); //程序并未添加Dogs这个程序集的引用,其和可执行程序在同一目录下即可 //反射程序集,动态创建其类型实例,并调用其方法 Type dogType = Assembly.Load("Dogs").GetType("Dogs.Dog");//注意:完全限定名,困惑了好久! dynamic dog = Activator.CreateInstance(dogType); dog.Speak(); Console.ReadKey(); } private static object GetASpeaker() { return new Employee() { FirstName = "DebugLZQ" }; } } }
Dogs类库如下:
using System; namespace Dogs { public class Dog { public void Speak() { Console.WriteLine("Woof"); } } }
其编译后为Dogs.dll。
using System; using System.Dynamic; namespace ExpandoSample { class Program { static void Main(string[] args) { //ExpandoObject:表示一个对象,该对象包含可在运行时动态添加和移除的成员。 dynamic expando=new ExpandoObject(); expando.Name = "DebugLZQ"; expando.Speak = new Action(()=>Console.WriteLine("My name is {0}",expando.Name )); expando.Speak(); Console.ReadKey(); } } }
要了解更加深入的方案,请看关于 ExpandoObject 和 DynamicObject 类的 MSDN 文档。同时,还有一些值得一看的文章,比如由 Bill Wagner 撰写的文章“动态方法包 。
C# 团队在 C# 4 版本中专门考虑的 COM 互操作方案是针对 Microsoft Office 应用程序(如 Word 和 Excel)进行编程。他们的目的是让这一任务在 C# 中变得像在 Visual Basic 中那样容易和自然。
using System; using System.Diagnostics; namespace dynamicExcel { class Program { static void Main(string[] args) { Type excelType = Type.GetTypeFromProgID("Excel.Application"); dynamic excel = Activator.CreateInstance(excelType); excel.Visible = true; excel.Workbooks.Add(); dynamic sheet = excel.ActiveSheet; Process[] processes = Process.GetProcesses(); for (int i = 0; i < processes.Length ; i++) { sheet.Cells[i + 1, "A"] = processes[i].ProcessName; sheet.Cells[i + 1, "B"] = processes[i].Threads.Count; } } } }
使用它可以为自己的库提供更好的语法,或为现有库创建包装。
using System; using System.Xml.Linq; namespace EncodXML { class Program { static void Main(string[] args) { //处理XML基本就这三种框架 //1.XmlDocument //... //2.LINQ to XML //... //3.XDocument var doc = XDocument.Load("Employees.xml"); foreach (var employee in doc.Element("Employees").Elements("Employee")) { Console.WriteLine(employee.Element("FirstName").Value ); } //Dynamic包装-提供更简洁的语法 var doc2 = XDocument.Load("Employees.xml").AsExpando(); foreach (var employee in doc2.Employees) { Console.WriteLine(employee.FirstName); } } } }
using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using System.Dynamic; namespace EncodXML { public static class ExpandoXML { public static dynamic AsExpando(this XDocument xDocument) { return CreateExpando(xDocument.Root); } private static dynamic CreateExpando(XElement element) { var result = new ExpandoObject() as IDictionary<string, object>; if(element.Elements().Any(e=>e.HasElements )) { var list = new List<ExpandoObject>(); result.Add(element.Name.ToString(),list); foreach(var childElement in element.Elements()) { list.Add(CreateExpando(childElement )); } } else { foreach (var leafElement in element.Elements()) { result.Add(leafElement.Name.ToString(),leafElement.Value ); } } return result; } } }
<?xml version="1.0" encoding="utf-8" ?> <Employees> <Employee> <FirstName>DebugLZQ</FirstName> </Employee> <Employee> <FirstName>DebugLZQ</FirstName> </Employee> <Employee> <FirstName>DebugLZQ</FirstName> </Employee> <Employee> <FirstName>DebugLZQ</FirstName> </Employee> <Employee> <FirstName>DebugLZQ</FirstName> </Employee> <Employee> <FirstName>DebugLZQ</FirstName> </Employee> </Employees>
关于反射与Dynamic使用方法对照
using System; using System.Reflection; namespace LoadAssembly { class Program { static void Main(string[] args) { Type dogType = Assembly.Load("Dogs").GetType("Dogs.Dog"); dynamic dog = Activator.CreateInstance(dogType); dog.Speak(); Console.ReadKey(); object dog2 = Activator.CreateInstance(dogType); dog2.GetType().GetMethod("Speak").Invoke(dog2, null); Console.ReadKey(); } } }
关于Dynamic优化反射性能请看:浅谈.NET反射机制的性能优化。