原文:http://www.ibeifeng.com/tech.php?id=62771

dynamic 关键字和动态语言运行时 (DLR) 是 C# 4 和 Microsoft .NET Framework 4 中的重大新增功能。 这样,人们在各种论坛和会议上总是一遍又一遍地提出相同的问题。

我还将提供指向相关内容的大量链接,供您进一步阅读。

  什么是“动态”?

C# 和 Java 经常被认为是静态类型化语言的例子,而 Python、Ruby 和 JavaScript 是动态类型化语言的例子。

这种方法有利有弊:代码编写起来往往更快、更容易,但同时,由于您不会获得编译器错误,只能通过单元测试和其他方法来确保应用程序正常运行。

C# 团队考虑了多种设计选项,但最终确定添加一个新关键字来支持这些功能:dynamic。

尤其是,动态对象被认定是 C# 语言中的“一等公民”,因此没有用于打开或关闭动态功能的选项,并且没有向 C# 添加过类似于 Visual Basic 中的 Option Strict On/Off 之类的功能。

下面是一个常见示例:

 

dynamic d ="test"; Console.WriteLine(d.GetType()); // Prints "System.String". d =100; Console.WriteLine(d.GetType()); // Prints "System.Int32".
 

不过,下面的代码也会通过编译,但在运行时会引发异常:

 

dynamic d ="test"; // The following line throws an exception at run time.d++;
 

  原因是相同的:编译器不知道该对象的运行时类型,因此无法告诉您递增操作在此情况下不受支持。

正如在用于 Visual Studio 的 IronPython 工具中那样,通过附加的类型推断可能会解决此问题,但目前 C# 不提供这种类型推断。

本文在后面将对这一问题进行更详细的讨论。

  Dynamic、Object 还是 Var?

下面是每个关键字的简短定义和一些示例。

此关键字经常在编译时无法确定对象类型时使用,而这种情况经常在各种互操作性情形中发生。

  您需要使用显式转换将已声明为 object 的变量转换为特定类型:

 

object objExample =10; Console.WriteLine(objExample.GetType());
 

但是,因为静态类型为 System.Object,所以您在这里需要一个显式转换:

 

objExample = (int)objExample +10;

  您可以赋予不同类型的值,因为它们都是从 System.Object 继承的:

 

objExample ="test"

如果编译器不能推断类型,它会生成一个编译错误:

 

var varExample =10; Console.WriteLine(varExample.GetType());
 

  这段代码会输出 System.Int32,与静态类型相同。

  在下面的示例中,因为 varExample 的静态类型为 System.Int32,所以不需要转换:

 

varExample = varExample +10;
 

  下面一行不进行编译,因为只能将整数赋给 varExample:

 

varExample ="test";
 

实际上,动态类型在后台使用 System.Object 类型。但与 object 不同的是,动态类型不需要在编译时执行显式转换操作,因为它仅在运行时识别类型:

 

dynamic dynamicExample =10; Console.WriteLine(dynamicExample.GetType());
 

  此段代码会输出 System.Int32。

  在下面这一行中不需要转换,因为仅在运行时识别类型:

 

dynamicExample = dynamicExample +10;

  可以将不同类型的值赋给 dynamicExample:

 

dynamicExample ="test";

在 C# 常见问题解答博客 (bit.ly/c95hpl) 上,提供了关于关键字 object 和 dynamic 之间差别的详细博客文章。

例如,我们来看一看下面的代码:

 

dynamic dynamicObject =new Object(); var anotherObject = dynamicObject;
 

务必要知道,var 关键字不过是一个指令,它让编译器根据变量的初始化表达式推断类型;var 不是类型。

  动态语言运行时

虽然这两个概念是相关的,但也务必要了解它们之间的差别。

其次,它将动态行为引入 C# 和 Visual Basic 之中。

dlr.codeplex.com 上提供。

但是,如果您希望实现新的动态语言或将其迁移到 .NET,则可以获益于开源项目中额外的帮助程序类,该开源项目为语言实现人员提供了更多功能和服务。

  在静态类型化语言中使用 Dynamic

而且,再次指出,C# 中的动态对象不支持 IntelliSense,这对总体工作效率可能会有些影响。

下面是一些例子。

此时使用动态功能可能比使用反射更加容易和方便。

channel9.msdn.com/pdc2008/TL16) 上提供了一个极好的例子,如下所示:

 

object calc = GetCalculator();
 Type calcType = calc.GetType(); object res = calcType.InvokeMember( "Add", BindingFlags.InvokeMethod, null, newobject[] { 10, 20 }); int sum = Convert.ToInt32(res);

请注意,此方法无法使用 IntelliSense,因为您以字符串文本的形式提供了方法名称。

  使用 dynamic 关键字,代码就很简单了:

 

dynamic calc = GetCalculator(); int sum = calc.Add(10, 20);

假设情况没有变化:存在某种我们希望其具有 Add 方法的未知类型的对象。 但语法阅读和使用起来要容易很多,看上去就像在调用一个普通的 .NET 方法。

  动态方法包

  可以利用动态功能的另外一个例子是创建动态方法包,动态方法包就是可在运行时添加和删除属性及方法的对象。

System.Dynamic.ExpandoObject 和 System.Expando.DynamicObject 类与新的 dynamic 关键字相结合,有助于以清晰和易于阅读的方式来创建动态结构和层次结构。

  例如,下面说明了如何使用 ExpandoObject 类来添加属性和方法:

 

dynamic expando =new ExpandoObject();
expando.SampleProperty ="This property was added at run time";
expando.SampleMethod = (Action)(() => Console.WriteLine(expando.SampleProperty));
 expando.SampleMethod();

bit.ly/amRYRw)。

  类包装

与前两个方案相比,这是一个更高级的方案,并且需要对 DLR 具体内容有更深入的了解。

这样,您就可以在一个类属性中存储一个要为其提供更佳语法的对象,但通过动态调度来处理针对该对象的所有操作。

  例如,请看一下图 1 中的 DynamicString 类,该类包装了一个字符串,并在通过反射实际调用所有方法之前显示这些方法的名称。

1 public class DynamicString : DynamicObject {  string str;   public DynamicString(string str) {    this.str = str;  }   public override bool TryInvokeMember(    InvokeMemberBinder binder, object[] args,     out object result) {    Console.WriteLine("Calling method: {0}", binder.Name);    try {      result = typeof(string).InvokeMember(        binder.Name,        BindingFlags.InvokeMethod |        BindingFlags.Public |        BindingFlags.Instance,        null, str, args);      return true;    }    catch {      result = null;      return false;    }  }}  
2
3
4   若要实例化该类,应使用 dynamic 关键字:
5
6
7
8 dynamic dStr = new DynamicString("Test");Console.WriteLine(dStr.ToUpper()); Console.ReadLine();


当然,这个特定示例出于演示目的而设计,不具有实际效率。 但是,如果您拥有已严重依赖于反射的 API,就可以如此处所示将所有通过反射进行的调用打包,以便针对 API 的最终用户隐藏这些调用。

bit.ly/dgS3od)。

dlr.codeplex.com)。

  可编写脚本的应用程序

现在,DLR 提供了一组公用的语言宿主 API,因此可让您创建可编写脚本的应用程序。

  例如,您可以创建一个应用程序,使用户能够自己在其中添加功能而不需要主产品提供新功能,例如向游戏中添加新的字符和映射,或向业务应用程序添加新的图表。

然而,实际上任何语言都可以支持这些 API,包括不是在 DLR 之上实现的语言。

microsoftpdc.com/2009/FT30) 上由 Dino Viehland 所做的演示“使用动态语言生成可编写脚本的应用程序”。

  识别动态对象

您可以将鼠标光标悬停在对象上以查看其声明类型,或检查 IntelliSense 是否可用。

了解 C# 4 中的 Dynamic 关键字

然而在运行时,情况会变得更加复杂。 这种情况与将变量声明为 object 时的情况相同:在运行时,您只能获取变量所存储的值的类型;无法判断此变量最初是否声明为 object。

知道这种情况可能十分重要,因为像 ExpandoObject 和 DynamicObject 类型的对象可在运行时改变其行为,例如,添加和删除属性及方法。

如果向 ExpandoObject 类的实例添加属性,则无法通过反射获取该属性:

dynamic expando = new ExpandoObject();expando.SampleProperty = "This property was added at run time";PropertyInfo dynamicProperty = expando.GetType().GetProperty("SampleProperty");// dynamicProperty is null.

有利的方面是,在 .NET Framework 4 中,所有可动态添加和删除成员的对象都必须实现一个特定接口:System.Dynamic.IDynamicMetaObjectProvider。 不过,这并不表示任何使用 dynamic 关键字声明的对象都实现此接口:

dynamic expando = new ExpandoObject();Console.WriteLine(expando is IDynamicMetaObjectProvider);// Truedynamic test = "test";Console.WriteLine(test is IDynamicMetaObjectProvider);// False

 

因此,如果将动态功能与反射一起使用,则请记住,反射不适用于动态添加的属性和方法,并且最好检查正在反射的对象是否实现了 IDynamicMetaObjectProvider 接口。

  动态功能与 COM 互操作

这也是 Visual Basic 和 C# 共同发展策略的一部分,这个策略旨在实现两种语言的功能对等,并相互借鉴最佳、最具效率的解决方案。

bit.ly/bFUpxG)。

每行下面的注释显示了 C# 3.0 及更早版本的中的等效代码。

1 // Add this line to the beginning of the file:// using Excel = Microsoft.Office.Interop.Excel; var excelApp = new Excel.Application(); excelApp.Workbooks.Add();// excelApp.Workbooks.Add(Type.Missing); excelApp.Visible = true; Excel.Range targetRange = excelApp.Range["A1"];// Excel.Range targetRange = excelApp.get_Range("A1", Type.Missing); targetRange.Value = "Name";// targetRange.set_Value(Type.Missing, "Name"); targetRange.Columns[1].AutoFit();// ((Excel.Range)targetRange.Columns[1, Type.Missing]).AutoFit(); 
2   此示例有趣的地方是,您在代码中的任何位置都看不到 dynamic 关键字。实际上,该关键字仅在下面一行中用到:
3
4
5
6 targetRange.Columns[1].AutoFit();// ((Excel.Range)targetRange.Columns[1, Type.Missing]).AutoFit();

 在 C# 3.0 版中,targetRange.Columns[1, Type.Missing] 返回 object,这便是需要向 Excel.Range 转换的原因。 因此,C# 4 中 targetRange.Columns[1] 的类型实际上是 dynamic。

msdn.microsoft.com/magazine/ff796223) 中对这些新增功能做了很好的概述。

  从哪里可以获取更多信息?

我们拥有一个活跃的社区,欢迎新成员加入。

相关文章:

  • 2022-12-23
  • 2021-08-16
  • 2022-12-23
  • 2022-02-24
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2021-12-30
  • 2021-07-19
  • 2022-12-23
  • 2021-07-25
  • 2022-12-23
相关资源
相似解决方案