当前位置:首页 > Windows程序 > 正文

.NET Framework 4 中的新 C# 功能

2021-03-27 Windows程序

C# 编程语言自 2002 年初次发布以来已经有了极大的改善,可以帮助程序员编写更清晰易懂、更容易维护的代码。这种改善来自于不断加入的新功能,例如泛型类型、可为空的值类型、lambda 表达式、迭代器方法、分部类以及其他大量有用的语言结构。而且,这些改变还经常伴随着为 Microsoft .NET Framework 库提供相应的支持。

C# 4.0 延续了这种不断提高易用性的趋势。这款产品大大简化了许多常见任务,包括泛型类型、传统的互操作以及处理动态对象模型。本文旨在为深入调查探讨这些新功能。我将先介绍泛型方差,然后探讨传统互操作特性和动态互操作特性。

协变与逆变

协变与逆变最好通过示例来介绍,而最好的示例就在框架中。在 System.Collections.Generic 中,IEnumerable<T> 和 IEnumerator<T> 分别表示一个包含 T 的序列的对象和一个用来遍历该序列的枚举器(或迭代器)。这些接口长期以来承担了大量繁重的任务,因为它们允许实现 foreach 循环构造。在 C# 3.0 中,它们变得更加突出,因为它们在 LINQ 和 LINQ 到对象中起着重要作用:它们是表示序列的 .NET 接口。

因此,假如您有一个类层次结构,其中包含一个 Employee 类型以及从这个 Employee 类型派生而来的 Manager 类型(毕竟经理也是员工),那么您认为以下代码会产生什么效果?

 

IEnumerable<Manager> ms = GetManagers(); IEnumerable<Employee> es = ms;

看起来好像应该能够将 Manager 序列当作 Employee 序列。但是在 C# 3.0 中,赋值操作将失败;编译器将提示您,没有相应的转换功能。毕竟,该版本根本不能理解 IEnumerable<T> 的语义。这可以是任何接口,因此对于任意接口 IFoo<T>,为什么就能说 IFoo<Manager> 基本上能替代 IFoo<Employee> 呢?

而在 C# 4.0 中,赋值操作是有效的,因为 IEnumerable<T> 以及其他几种接口均发生了变化,这种变化是由 C# 中新增的类型参数协变支持实现的。

IEnumerable<T> 比任意 IFoo<T> 更加特殊,因为尽管初看起来并不明显,但是使用类型参数 T 的成员(IEnumerable<T> 中的 GetEnumerator 和 IEnumerator<T> 中的 Current 属性)实际上仅在返回值的位置使用 T。因此,您只能从序列中获取 Manager,而绝不能向其中放入 Manager。

相比之下,让我们看看 List<T>。由于以下原因,用 List<Manager> 来替代 List<Employee> 将是一场灾难:

 

List<Manager> ms = GetManagers(); List<Employee> es = ms; // Suppose this were possible es.Add(new EmployeeWhoIsNotAManager()); // Uh oh

正如这段代码所示,如果您认为您是在查看 List<Employee>,那您就可以插入任何员工。但实际上正在操作的列表是 List<Manager>,因此插入非 Manager 的员工一定会失败。如果允许这种操作,就会失去类型安全性。List<T> 不能对 T 进行协变。

而 C# 4.0 中有一项新的语言功能,允许定义一些类型(例如新的 IEnumerable<T>),只要正在处理的类型参数之间存在一定的关系,就允许在这些类型参数之间进行转换。.NET Framework 开发人员编写 IEnumerable<T> 时就使用了这项特性,他们的代码类似于下面的代码(当然经过了简化):

 

public interface IEnumerable<out T> { /* ... */ }

请注意修饰类型参数 T 的定义的 out 关键字。当编译器遇到此关键字时,会将 T 标记为协变,并检查接口定义中使用的 T 是否均正常(即,它们是否仅在输出位置使用,这也就是 out 关键字的由来)。

为什么将这种特性称为协变呢?通过绘制箭头,最容易看出原因。为了更形象,还是让我们使用 Manager 和 Employee 类型。由于这两个类之间存在继承关系,从 Manager 到 Employee 之间存在隐式引用转换。

Manager → Employee

现在,由于 IEnumerable<out T> 中 T 的注释,从 IEnumerable<Manager> 到 IEnumerable<Employee> 之间也存在隐式引用转换。这就是 T 注释的目的:

IEnumerable<Manager> → IEnumerable<Employee>

这被称为协变,因为两个示例中的箭头都指向相同的方向。我们首先定义两个类型 Manager 和 Employee。然后利用这两个类型来定义新类型 IEnumerable<Manager> 和 IEnumerable<Employee>。新类型的转换方式与旧类型一样。

当这两种转换的方向相反时,即为逆变。您可能已经想到这种情况将发生在仅将类型参数 T 用作输入时,那您就对了。例如,System 命名空间包含一个名为 IComparable<T> 的接口,该接口有一个名为 CompareTo 的方法:

 

public interface IComparable<in T> { bool CompareTo(T other); }

温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/68438.html