什么是C#4.0中使用的“dynamic”types?

C#4.0引入了一个名为“dynamic”的新types。 这听起来不错,但是程序员会用它来做什么呢?

有没有可以挽救一天的情况?

dynamic关键字是C#4.0的新增function,用于告诉编译器variables的types可以更改,或者直到运行时才知道。 把它看作是能够与一个对象进行交互而不必投射它。

dynamic cust = GetCustomer(); cust.FirstName = "foo"; // works as expected cust.Process(); // works as expected cust.MissingMethod(); // No method found! 

注意,我们不需要投入或声明客户types为客户。 因为我们声明它是dynamic的,所以运行时会接pipe并为我们search并设置FirstName属性。 当然,当你使用dynamicvariables的时候,你放弃了编译器types检查。 这意味着调用cust.MissingMethod()将编译并且不会失败,直到运行时。 此操作的结果是RuntimeBinderException,因为MissingMethod未在Customer类中定义。

上面的例子显示了调用方法和属性时dynamic如何工作。 另一个强大的(也是潜在的危险)function是能够为不同types的数据重用variables。 我敢肯定,那里的Python,Ruby和Perl程序员可以想象有一百万种方式来利用这个优势,但是我一直在使用C#,以至于我觉得这是错误的。

 dynamic foo = 123; foo = "bar"; 

好的,所以你很可能不会经常像上面那样编写代码。 然而,可能有时候,variables重用可以派上用场,或者清理一块脏的旧代码。 我经常碰到的一个简单的例子就是不断的在十进制和双精度之间进行转换。

 decimal foo = GetDecimalValue(); foo = foo / 2.5; // Does not compile foo = Math.Sqrt(foo); // Does not compile string bar = foo.ToString("c"); 

第二行不编译,因为2.5被input为双精度型,第3行不能编译,因为Math.Sqrt需要双精度型。 显然,你所要做的就是投射和/或改变你的variablestypes,但是可能会出现dynamic使用的情况。

 dynamic foo = GetDecimalValue(); // still returns a decimal foo = foo / 2.5; // The runtime takes care of this for us foo = Math.Sqrt(foo); // Again, the DLR works its magic string bar = foo.ToString("c"); 

阅读更多function: http : //www.codeproject.com/KB/cs/CSharp4Features.aspx

添加了dynamic关键字以及C#4.0的许多其他新function,以便与其他运行时或来自其他运行时的代码进行对话,这些代码具有不同的API。

举一个例子。

如果您有一个COM对象,如Word.Application对象,并且想要打开一个文档,那么执行此操作的方法将带有不less于15个参数,其中大多数参数是可选的。

要调用这个方法,你需要这样的东西(我正在简化,这不是实际的代码):

 object missing = System.Reflection.Missing.Value; object fileName = "C:\\test.docx"; object readOnly = true; wordApplication.Documents.Open(ref fileName, ref missing, ref readOnly, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing); 

注意所有这些论点? 因为C#版本4.0之前没有可选参数的概念,所以你需要通过这些。 在C#4.0中,COM API通过引入:

  1. 可选参数
  2. 为COM API制作ref可选的
  3. 命名的参数

上述调用的新语法是:

 wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true); 

看看它看起来容易多less,它变得多可读?

让我们分开:

  named argument, can skip the rest | v wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true); ^ ^ | | notice no ref keyword, can pass actual parameter values instead 

奇妙的是,C#编译器现在会注入必要的代码,并且在运行时使用新的类来完成与之前完全相同的事情,但是语法已经隐藏起来,现在您可以关注什么 ,而不是那么多的如何 。 安德斯·海斯尔斯伯格喜欢说你必须引用不同的“咒语”,这是对整个事物魔术的一种双关语,在这种情况下,你通常必须挥挥手,并按照正确的顺序说出一些神奇的单词得到某种types的法术。 与COM对象交stream的旧API方法是很多的,你需要跳过很多环节来哄编译器为你编译代码。

如果你试图和一个没有接口或类的COM对象进行交谈,那么在C#版本4.0之前的事情就会崩溃,你所拥有的就是一个IDispatch引用。

如果你不知道它是什么,那么IDispatch基本上就是COM对象的reflection。 使用IDispatch接口,您可以询问对象“什么是称为Save的方法的ID号”,并构build包含参数值的某种types的数组,最后在IDispatch接口上调用一个Invoke方法来调用方法,把所有你设法拼凑的信息都传递给你。

上面的Save方法可能看起来像这样(这绝对不是正确的代码):

 string[] methodNames = new[] { "Open" }; Guid IID = ... int methodId = wordApplication.GetIDsOfNames(IID, methodNames, methodNames.Length, lcid, dispid); SafeArray args = new SafeArray(new[] { fileName, missing, missing, .... }); wordApplication.Invoke(methodId, ... args, ...); 

所有这一切只是打开一个文件。

VB有很多可选的参数,并且很早以前支持这个开箱即用的,所以这个C#代码:

 wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true); 

基本上就是C#在performance力方面追赶VB,但是这样做是正确的,通过扩展,而不仅仅是COM。 当然,这也适用于VB.NET或.NET运行库之上的任何其他语言。

您可以在Wikipedia上find更多关于IDispatch接口的信息:IDispatch,如果您想了解更多信息。 这真是血淋淋的东西。

但是,如果您想与Python对象交谈呢? 这里有一个不同于用于COM对象的API,由于Python对象本质上也是dynamic的,所以你需要使用reflection魔术来find正确的方法来调用它们的参数,而不是.NET。reflection,为Python编写的东西,非常像上面的IDispatch代码,只是完全不同。

对于Ruby? 还有一个不同的API。

JavaScript的? 同样的交易,不同的API也是如此。

dynamic关键字由两部分组成:

  1. C#中的新关键字, dynamic
  2. 一组知道如何处理不同types对象的运行时类,它们实现了dynamic关键字所需的特定API,并将调用映射到正确的方式。 这个API甚至被logging下来,所以如果你有没有包含来自运行时的对象,你可以添加它。

然而, dynamic关键字不是要取代任何现有的纯.NET代码。 当然,你可以这样做,但不是因为这个原因才添加的,而且与Anders Hejlsberg在一起的C#编程语言的作者最为坚定,他们仍然认为C#是一种强types语言,并且不会牺牲这个原则。

这意味着,虽然你可以写这样的代码:

 dynamic x = 10; dynamic y = 3.14; dynamic z = "test"; dynamic k = true; dynamic l = x + y * z - k; 

并进行编译,这并不意味着在运行时types的系统中使用了一种“魔法让我们无法理解”的意思。

整个目的是为了更容易与其他types的对象交谈。

互联网上有关于关键词,支持者,反对者,讨论,咆哮,赞美等的大量资料。

我build议你从以下链接开始,然后谷歌更多:

  • 开发日2010:安德斯Hejlsberg – C#4.0及以上
  • 频道9:Mads Torgersen – C#4.0内部:dynamicinput+ +
  • DevX:COM Interop在C#4.0中变得更好
  • Scott Hanselman – C#4和dynamic关键字 – 围绕.NET 4(和Visual Studio 2010)Beta 1的Whirlwind Tour

它使静态types化语言(CLR)与在DLR(dynamic语言运行库)上运行的dynamictypes(python,ruby …)更容易互操作,请参阅MSDN 。

我很惊讶,没有人提到多次派遣 。 解决这个问题的常用方法是通过访问者模式 ,这并不总是可能的,所以你最终得到堆叠检查。

所以这里是我自己的一个应用程序的真实生活的例子。 而不是做:

 public static MapDtoBase CreateDto(ChartItem item) { if (item is ElevationPoint) return CreateDtoImpl((ElevationPoint)item); if (item is MapPoint) return CreateDtoImpl((MapPoint)item); if (item is MapPolyline) return CreateDtoImpl((MapPolyline)item); //other subtypes follow throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType()); } 

你做:

 public static MapDtoBase CreateDto(ChartItem item) { return CreateDtoImpl(item as dynamic); } private static MapDtoBase CreateDtoImpl(ChartItem item) { throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType()); } private static MapDtoBase CreateDtoImpl(MapPoint item) { return new MapPointDto(item); } private static MapDtoBase CreateDtoImpl(ElevationPoint item) { return new ElevationDto(item); } 

请注意,在第一种情况下, ElevationPointMapPoint子类,如果它不放在MapPoint 之前 ,它将永远不会到达。 dynamic的情况并非如此,因为最接近的匹配方法将被调用。

正如你可能从代码中猜测的那样,当我从ChartItem对象转换到它们的可序列化版本的时候,这个特性很方便。 我不想污染我的代码与访问者,我也不想污染我的ChartItem对象与无用的序列化特定的属性。

COM互操作。 尤其是IUnknown。 它是专门为它devise的。

使用示例:

你消耗了许多具有共同属性“CreationDate”的类:

 public class Contact { // some properties public DateTime CreationDate { get; set; } } public class Company { // some properties public DateTime CreationDate { get; set; } } public class Opportunity { // some properties public DateTime CreationDate { get; set; } } 

如果你写一个通信方法来检索'CreationDate'属性的值,你必须使用reflection:

  static DateTime RetrieveValueOfCreationDate(Object item) { return (DateTime)item.GetType().GetProperty("CreationDate").GetValue(item); } 

用“dynamic”的概念,你的代码更优雅:

  static DateTime RetrieveValueOfCreationDate(dynamic item) { return item.CreationDate; } 

RAD和Python受害者主要使用它来破坏代码质量, IntelliSense和编译时错误检测。

它在运行时进行评估,所以你可以像使用JavaScript一样将types切换到任何你想要的。 这是合法的:

 dynamic i = 12; i = "text"; 

所以你可以根据需要改变types。 用它作为最后的手段; 这是有益的,但是我听说在产生IL的情况下会有很多的事情发生,并且可能会以性能价格出现。

  1. 您可以使用pythonnet调用dynamic语言,如CPython:

dynamic np = Py.Import("numpy")

  1. 在对其应用数字运算符时,可以将generics转换为dynamic 。 这提供了types安全性并避免了generics的限制。 这本质上是鸭子打字:

T y = x * (dynamic)x ,其中typeof(x) is T