泛型--协变与逆变(转)

对于泛型的知识,一直比较模糊,现在有机会整理一下,突发发现C#还有很多你不知道的东东,继续.NET FrameWork中泛型的协变与逆变:

1. 可变性的类型:协变性和逆变性

可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用。如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量。协变和逆变是两个相互对立的概念:

  • 如果某个返回的类型可以由其派生类型替换,那么这个类型就是支持协变的
  • 如果某个参数类型可以由其基类替换,那么这个类型就是支持逆变的。

2. C# 4.0对泛型可变性的支持

在C# 4.0之前,所有的泛型类型都是不变量——即不支持将一个泛型类型替换为另一个泛型类型,即使它们之间拥有继承关系,简而言之,在C# 4.0之前的泛型都是不支持协变和逆变的。

C# 4.0通过两个关键字:out和in来分别支持以协变和逆变的方式使用泛型。

3. C#中泛型可变性的限制

1. 不支持类的类型参数的可变性

只有接口和委托可以拥有可变的类型参数。in 和 out 修饰符只能用来修饰泛型接口和泛型委托。

2. 可变性只支持引用转换

可变性只能用于引用类型,禁止任何值类型和用户定义的转换,如下面的转换是无效的:

  • 将 IEnumerable<int> 转换为 IEnumerable<object> ——装箱转换
  • 将 IEnumerable<short> 转换为 IEnumerable<int> ——值类型转换
  • 将 IEnumerable<string> 转换为 IEnumerable<XName> ——用户定义的转换

3. 类型参数使用了 out 或者 ref 将禁止可变性

对于泛型类型参数来说,如果要将该类型的实参传给使用 out 或者 ref 关键字的方法,便不允许可变性,如:

delegate void someDelegate<in T>(ref T t)

4. 可变性必须显式指定

从实现上来说编译器完全可以自己判断哪些泛型参数能够逆变和协变,但实际却没有这么做,这是因为C#的开发团队认为:

必须由开发者明确的指定可变性,因为这会促使开发者考虑他们的行为将会带来什么后果,从而思考他们的设计是否合理。

5. 注意破坏性修改

在修改已有代码接口的可变性时,会有破坏当前代码的风险。例如,如果你依赖于不允许可变性的is或as操作符的结果,运行在.NET 4时,代码的行为将有所不同。同样,在某些情况下,因为有了更多可用的选项,重载决策也会选择不同的方法。所以在对已有代码引入可变性时要做好足够的单元测试以及防御措施。

6. 多播委托与可变性不能混用

下面的代码能够通过编译,但是在运行时会抛出 ArgumentException 异常:

Func<string> stringFunc = () => "";
Func<object> objectFunc = () => new object();
Func<object> combined = objectFunc + stringFunc;

这是因为负责链接多个委托的 Delegate.Combine方法要求参数必须为相同的类型。上面的示例我们可以修改成如下正确的代码:

Func<string> stringFunc = () => "";
Func<object> defensiveCopy = new Func<object>(stringFunc);
Func<object> objectFunc = () => new object();
Func<object> combined = objectFunc + defensiveCopy;
时间: 2024-10-11 18:29:13

泛型--协变与逆变(转)的相关文章

.NET 4.0中的泛型协变和逆变

随Visual Studio 2010 CTP亮相的C#4和VB10,虽然在支持语言新特性方面走了相当不一样的两条路:C#着重增加后期绑定和与动态语言相容的若干特性,VB10着重简化语言和提高抽象能力:但是两者都增加了一项功能:泛型类型的协变(covariant)和反变(contravariant).许多人对其了解可能仅限于增加的in/out关键字,而对其诸多特性有所不知.下面我们就对此进行一些详细的解释,帮助大家正确使用该特性. 背景知识:协变和反变 很多人可能不不能很好地理解这些来自于物理和

泛型的协变和逆变

转载C# 泛型的协变和逆变 1. 可变性的类型:协变性和逆变性 可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用.如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量.协变和逆变是两个相互对立的概念: 如果某个返回的类型可以由其派生类型替换,那么这个类型就是支持协变的 如果某个参数类型可以由其基类替换,那么这个类型就是支持逆变的. 2. C# 4.0对泛型可变性的支持 在C# 4.0之前,所有的泛型类型都是不变量——即不支持将一个泛型类型替换为另一个泛型类型,即使它们之间

泛型中的协变和逆变

[泛型中的协变和逆变] 协变指能够使用比原始指定的派生类型的派生程度更大的类型,逆变指能够使用比原始指定的派生类型的派生程度更小的类型. 协变与逆变的本质就是参数的替换.逻辑不变,只进行参数的替换,以实现更高程序的复用. 通常,协变类型参数可用作委托的返回类型,而逆变类型参数可用作参数类型. 对于接口,协变类型参数可用作接口的方法的返回类型,而逆变类型参数可用作接口的方法的参数类型. 协变是out,逆变是in. 协变的例子: 逆变的例子,When the delegate of type Act

协变、逆变与不变:数组、泛型、与返回类型

转自:http://blog.csdn.net/yi_Afly/article/details/52071260 1. 前言 之前几篇博文,有些地方涉及到了协变性.逆变性与不变性在Java中的表现,所以这篇博文将重点记录这方面的内容,并辅以JDK源码中的一些实例,加以说明. 2. 定义 这里讨论的协变.逆变与不变都是编程语言中的概念.下面介绍定义: 若类A是类B的子类,则记作A ≦ B.设有变换f(),若: 当A ≦ B时,有f(A)≦ f(B),则称变换f()具有协变性. 当A ≦ B时,有f

厘清泛型参数的协变与逆变

协变与逆变(CoVariant and ContraVariant),很多人是糊涂的,我也一直糊涂.其实,对协变与逆变概念糊涂,甚至完全不知道,对一般程序员也没有很大影响.不过,如果你想提高水平,想大概看懂.Net Framework类库中那些泛型接口与泛型类,想大概弄清楚Linq,这个概念还是需要搞清楚. 话又说回来,想弄清楚,其实还是挺费劲的. 如果你还糊涂着这两个概念,相信我,认真看完下面的文字,你会对泛型参数的协变与逆变有一个清晰的理解. 想透彻掌握协变.逆变的概念,首先需要对接口.委托

.NET泛型03,泛型类型的转换,协变和逆变

协变(Convariant)和逆变(Contravariant)的出现,使数组.委托.泛型类型的隐式转换变得可能. 子类转换成基类,称之为协变:基类转换成子类,称之为逆变..NET4.0以来,支持了泛型接口的协变和逆变. 泛型协变 如果子类泛型隐式转换成基类泛型,使用泛型协变. 有这样的2个基类和派生类. public class Animal { public virtual void Write() { Console.WriteLine("我是基类"); } } public c

Java泛型中的协变和逆变

Java泛型中的协变和逆变 一般我们看Java泛型好像是不支持协变或逆变的,比如前面提到的List<Object>和List<String>之间是不可变的.但当我们在Java泛型中引入通配符这个概念的时候,Java 其实是支持协变和逆变的. 看下面几行代码: // 不可变 List<Fruit>fruits =newArrayList<Apple>();// 编译不通过 // 协变 List<?extendsFruit>wildcardFruit

泛型中协变和逆变

写在前面 今天讲的内容有点多,但是差不多都能听懂,稍微有点模糊的就是协变和逆变的概念,下面是我结合在网上看的资料整合而成的. 正文 msdn上的原话: 协变:是指能够使用比原始指定的派生类型的派生程度更小(不太确定)的类型 逆变:是指能够使用比原始类型的派生类型的派生程度更大(更具体)的类型 在方便理解的概念是: 协变:子类向父类转化,用于返回类型用out关键字 逆变:父类向子类转化的过程,用于方法参数类型用in关键字 协变的例子: 1 public class Person { } 2 3 p

Scala 协变 和 逆变

在Scala(以及其他许多编程语言)中,函数也是对象,可以使用.定义其他对象的地方,也可以使用.定义函数.Scala中的函数,具有apply方法的类的实例,就可以当做函数来使用.其中apply接受的参数就是函数的参数,而apply的返回值就是函数的返回值. 首先给出一个接受一个参数的函数的泛型定义. trait Function1[-T, +U] {   def apply(x: T): U } 这种函数接受一个参数,参数类型为泛型类型T,返回类型为泛型类型U.和其他支持泛型的语言一样,实际定义