添加了 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);
注意到所有这些论点了吗?你需要传递这些,因为在 4.0 版之前的 C# 没有可选参数的概念。在 C# 4.0 中,COM API 变得更易于使用,引入了:
- 可选参数
- 将
ref 设为 COM API 可选
- 命名参数
上述调用的新语法是:
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# 编译器现在将注入必要的代码,并在运行时使用新类,几乎与您之前所做的完全相同,但语法已对您隐藏,现在您可以关注what,而不是how。 Anders Hejlsberg 喜欢说你必须调用不同的“咒语”,这是对整个事情的魔力的一种双关语,你通常必须挥动你的手并以正确的顺序说出一些魔法词获得某种类型的咒语。与 COM 对象对话的旧 API 方式很多,您需要跳过很多圈才能哄骗编译器为您编译代码。
如果您尝试与没有接口或类的 COM 对象通信,那么在 4.0 版之前的 C# 中情况会更加糟糕,您所拥有的只是一个 IDispatch 引用。
如果你不知道它是什么,IDispatch 基本上是对 COM 对象的反射。使用IDispatch 接口,您可以询问对象“称为Save 的方法的ID 号是多少”,并构建包含参数值的特定类型的数组,最后在@987654339 上调用Invoke 方法@interface 来调用方法,传递你设法收集到的所有信息。
上面的 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 运行时之上的语言。
如果您想了解更多信息,可以在Wikipedia: IDispatch 上找到有关IDispatch 接口的更多信息。这真是血腥的东西。
但是,如果您想与 Python 对象对话怎么办?与用于 COM 对象的 API 不同,并且由于 Python 对象本质上也是动态的,因此您需要借助反射魔法来找到正确的调用方法、它们的参数等,而不是 .NET反射,为 Python 编写的东西,很像上面的 IDispatch 代码,只是完全不同。
对于 Ruby?仍然是不同的 API。
JavaScript?同样的交易,不同的 API 也是如此。
动态关键字由两部分组成:
- C# 中的新关键字
dynamic
- 一组运行时类,它们知道如何处理不同类型的对象,实现
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;
并让它编译,它并不是作为一种魔法让我们在运行时弄清楚你的意思是什么类型的系统。
整个目的是为了更容易与其他类型的对象交谈。
互联网上有大量关于关键字、支持者、反对者、讨论、咆哮、赞美等的材料。
我建议你从以下链接开始,然后谷歌了解更多: