C#4.0中的“动态”类型是用来做什么的?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (16)

C#4.0引入了一种名为'dynamic'的新类型。这听起来不错,但是程序员会用它做什么?

提问于
用户回答回答于

动态关键字是C#4.0的新增功能,用于告诉编译器变量的类型可以更改,或者直到运行时才知道。把它想象成能够与一个对象进行交互而不必投射它。

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

请注意,我们不需要将客户类型转换为客户,也不需要声明客户。因为我们声明它是动态的,运行时会接管并为我们搜索并设置FirstName属性。当然,当你使用动态变量时,你放弃了编译器类型检查。这意味着调用cust.MissingMethod()将进行编译,直到运行时才会失败。此操作的结果是RuntimeBinderException,因为MissingMethod未在Customer类中定义。

上面的例子显示了调用方法和属性时动态如何工作。另一个强大的(也是潜在的危险)功能是能够为不同类型的数据重用变量。我确信那里的Python,Ruby和Perl程序员可以想出一百万种方法来利用这一点,但是我一直在使用C#,以至于它感觉“错误”。

dynamic foo = 123;
foo = "bar";

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

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

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

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");
用户回答回答于

dynamic加入关键字,用C#4.0的许多其他新功能一起,以使其更简单谈谈代码,住在或来自其他运行时,有不同的API。

举一个例子。

如果你有一个COM对象,比如Word.Application对象,并且想要打开一个文档,那么这个方法的参数不少于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. 使refCOM API可选
  3. 命名的参数

上述调用的新语法是:

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

看看它变得容易多少,它变得多可读?

让我们分开:

                                    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#编译器现在会注入必要的代码,并且在运行时使用新类来完成与之前完全相同的操作,但是语法已经隐藏起来了,现在您可以将注意力集中在什么,而不是那么多的如何。Anders Hejlsberg喜欢说你必须引用不同的“咒语”,这是对整个事物的魔力的一种双关语,你通常必须挥动你的手并以正确的顺序说出一些神奇的单词获得某种类型的法术。与COM对象交流的旧API方法很多,您需要跳过很多环节才能哄骗编译器为您编译代码。

如果您尝试与没有接口或类的COM对象进行交谈,那么事情在C#版本4.0之前就会崩溃,您所拥有的仅仅是一个IDispatch参考。

如果你不知道它是什么,那么IDispatch基本上就是COM对象的反射。通过一个IDispatch接口,你可以询问对象“什么是称为Save的方法的id号码”,并建立一个包含参数值的特定类型的数组,最后调用接口Invoke上的一个方法IDispatch来调用该方法,传递所有你设法拼凑的信息。

上面的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#在表现力方面迎头赶上VB,但以正确的方式,通过扩展,而不仅仅是COM。当然,这也适用于VB.NET或任何其他建立在.NET运行时之上的语言。

如果您想了解更多信息,可以IDispatch维基百科上找到关于界面的更多信息:IDispatch。这真是血腥的东西。

但是,如果你想和一个Python对象交谈呢?对于那些用于COM对象的API有一个不同的API,并且由于Python对象本质上也是动态的,所以你需要求助于反射魔术来找到正确的方法来调用它们的参数等,而不是.NET。反射,为Python编写的东西,非常像上面的IDispatch代码,只是完全不同。

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

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

动态关键字包含两件事情:

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

dynamic关键字是没有,不过,无意取代任何现有的.NET的唯一的代码。当然,你可以这样做,但不是因为这个原因才添加的,而且与Anders Hejlsberg在一起的C#编程语言的作者一直坚持认为C#仍然是一种强类型语言,并且不会牺牲该原则。

这意味着尽管你可以编写这样的代码:

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

并进行编译,但这并不意味着它是一种在运行时类型的系统中的一种魔术 - 让我们不知道什么。

扫码关注云+社区