C# 中的动态类型

翻译自 Camilo Reyes 2018年10月15日的文章 《Working with the Dynamic Type in C#》

.NET 4 中引入了动态类型。动态对象使您可以处理诸如 JSON 文档之类的结构,这些结构的组成可能要到运行时才能知道。在本文中,Camilo Reyes 解释了如何使用动态类型。

.NET 4.0 中引入的 dynamic 关键字为 C# 编程带来了一个范式转变。对于 C# 程序员来说,强类型系统之上的动态行为可能会让人感到不适 —— 当您在编译过程中失去类型安全性时,这似乎是一种倒退。

动态编程可能使您面临运行时错误。声明一个在执行过程中会发生变化的动态变量是可怕的,当开发人员对数据做出错误的假设时,代码质量就会受到影响。

对 C# 程序员来说,避免代码中的动态行为是合乎逻辑的,具有强类型的经典方法有很多好处。通过类型检查得到的数据类型的良好反馈对于正常运行的程序是至关重要的,一个好的类型系统可以更好地表达意图并减少代码中的歧义。

随着动态语言运行时(Dynamic Language Runtime,DLR)的引入,这对 C# 意味着什么呢? .NET 提供了丰富的类型系统,可用于编写企业级软件。让我们来仔细看看 dynamic 关键字,并探索一下它的功能。

类型层次结构

公共语言运行时(Common Language Runtime,CLR)中的每种类型都继承自 System.Object,现在,请重复阅读这句话,直到将其铭记于心。这意味着 object 类型是整个类型系统的公共父类。当我们研究更神奇的动态行为时,这一事实本身就能为我们提供帮助。这里的想法是开发这种“代码感”,以便于您了解如何驾驭 C# 中的动态类型。

为了演示这一点,您可以编写以下程序:

Console.WriteLine("long inherits from ValueType: " + typeof(long).IsSubclassOf(typeof(ValueType)));

我将忽略 using 语句直到本文结束,以保持对代码示例的专注。然后,我再介绍每个命名空间及其作用。这样我就不必重复说过的话,并提供了一个回顾所有类型的机会。

上面的代码在控制台中的运算结果为 True。.NET 中的 long 类型是值类型,因此它更像是枚举或结构体。ValueType 重写来自 object 类的默认行为。ValueType 的子类在栈(stack)上运行,它们的生命周期较短,效率更高。

要验证 ValueType 是继承自 System.Object 的,请执行以下代码:

Console.WriteLine("ValueType inherits from System.Object: " + typeof(ValueType).IsSubclassOf(typeof(Object)));

它的运算结果为 True。这是一条可以追溯到 System.Object 的继承链。对于值类型,链中至少有两个父级。

再看一下从 System.Object 派生的另一个 C# 类型,例如:

Console.WriteLine("string inherits from System.Object: " + typeof(string).IsSubclassOf(typeof(Object)));

此代码在控制台中显示为 True。另一种从 object 继承的类型是引用类型,引用类型在堆(heap)上分配并进行垃圾回收,CLR 管理着引用类型,并在必要时从堆中释放它们。

查看下图,您可以直观地看到 CLR 的类型系统:

CLR’s type system

值类型和引用类型都是 CLR 的基本构建块,这种优雅的类型系统在 .NET 4.0 和动态类型之前就有了。我建议您在使用 C# 中的类型时,在脑海中记住这张图。那么,DLR 是如何适应这张图的呢?

动态语言运行时(DLR)

动态语言运行时(Dynamic Language Runtime, DLR)是处理动态对象的一种便捷方法。比如,假设您有 XML 或 JSON 格式的数据,其中的成员事先并不知道。DLR 允许您使用自然代码来处理对象和访问成员。

对于 C#,这使您可以处理在编译时不知道其类型的库。动态类型消除了自然 API 代码中的万能字符串。这就开启了像 IronPython 一样位于 CLR 之上的动态语言。

可以将 DLR 视为支持三项主要服务:

表达式树,来自 System.Linq.Expressions 命名空间。编译器在运行时生成具有动态语言互操作性的表达式树。动态语言超出了本文的讨论范围,这里就不作介绍了。

调用站点缓存,即缓存动态操作的结果。DLR 缓存像 a + b 之类的操作,并存储 a 和 b 的特征。当执行动态操作时,DLR 将检索先前操作中可用的信息。

动态对象互操作性是可用于访问 DLR 的 C# 类型。这些类型包括 DynamicObject 和 ExpandoObject。可用的类型还有很多,但是在处理动态类型时请注意这两种类型。

要了解 DLR 和 CLR 是如何结合在一起的,请看下图:

how the DLR and CLR fit together

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpyjgz.html