C#协变与逆变
http://zh.wikipedia.org/wiki/%E5%8D%8F%E5%8F%98%E4%B8%8E%E9%80%86%E5%8F%98
协变与逆变是程序设计语言中的类型系统的一对概念。类型系统支持子类型。例如,如果Cat是Animal的子类型,那么Cat类型的表达式可用于任何出现Animal类型表达式的地方。 变型(variance)是指,如何根据其组成类型来确定更复杂的类型(如Cat列表对Animal列表,返回Cat的函数对返回Animal的函数,...,等等。)之间的子类型关系。依赖于类型构造器的变型性质,复杂类型的子类型性质可分为保持、逆转、与忽略。例如,在C Sharp中:
- IEnumerable<Cat>是IEnumerable<Animal>的子类型,因为IEnumerable<T>类型构造器是协变(covariant)。也即,界面的最初的类型参数的子类型关系保持住了。
- Action<Cat>是Action<Animal>的超类型,因为Action<T>类型构造器是逆变(contravariant),其中Action<T>表示一个头等函数,需要一个类型参数为T或sub-T。注意对于T的子类型关系,在Action复杂类型封装下是逆转的。
- IList<Cat>或IList<Animal>彼此之间没有子类型关系。因为IList<T>类型构造器是不变(invariant).
程序语言的设计者需要考虑针对数组、继承、泛型数据类型等的类型规则的“变型”。通过使得类型构造器是协变、逆变而不是“不变”,使得更多的程序可作为良好类型被接受。另一方面,程序员经常觉得逆变是不直观的;如果为避免运行时刻错误而精确跟踪变型将导致复杂的类型规则。为了保持类型系统简单,允许有用的编程,程序设计语言可把类型构造器处理为“不变”,即使它其实作为“变型”也是类型安全的;或者把类型构造器处理为协变,即使这会导致违背类型安全。
using System; namespace TestCovarianceContravariance { public class Shape { public double Width { get; set; } public double Height { get; set; } public override string ToString() { return String.Format("Width: {0}, Height: {1}", Width, Height); } } public class Rectangle : Shape { } //如果泛型类型用out关键字标注,泛型接口就是协变的。这也意味着返回类型只能是T。 //接口IIndex与类型T是协变的,并从一个只读索引器中返回这个类型 public interface IIndex<out T> { T this[int index] { get; } int Count { get; } } public class RectangleCollection : IIndex<Rectangle> { private Rectangle[] data = new Rectangle[3] { new Rectangle {Height = 2, Width = 5}, new Rectangle {Height = 3, Width = 7}, new Rectangle {Height = 4.5, Width = 2.9} }; public static RectangleCollection GetRectangles() { return new RectangleCollection(); } public Rectangle this[int index] { get { if (index < 0 || index > data.Length) throw new ArgumentOutOfRangeException("index"); return data[index]; } } public int Count { get { return data.Length; } } } internal class Program { private static void Main(string[] args) { IIndex<Rectangle> rectangles = RectangleCollection.GetRectangles(); IIndex<Shape> shapes = rectangles; for (int i = 0; i < shapes.Count; i++) { Console.WriteLine(shapes[i]); } Console.WriteLine("##########"); IDisplay<Shape> shapeDisplay = new ShapeDisplay(); IDisplay<Rectangle> rectangleDisplay = shapeDisplay; rectangleDisplay.Show(rectangles[0]); Console.WriteLine("########## .net 框架中的示例 ###########"); /* public interface IEnumerable<out T> : IEnumerable //协变 public delegate void Action<in T>( //逆变 T obj ) */ Action<Shape> b = (target) => { Console.WriteLine(target.GetType().Name); }; Action<Rectangle> d = b; //逆变:Shape是Rectangle的超类型,而Action<Rectangle>却是Action<Shape>的超类型 d(new Rectangle()); b(new Shape()); Console.ReadKey(); } } public interface IDisplay<in T> //该类型参数是逆变。即可以使用指定的类型或派生程度更低的类型。 { void Show(T item); } public class ShapeDisplay : IDisplay<Shape> { public void Show(Shape s) { Console.WriteLine("{0} Width: {1}, Height: {2}", s.GetType().Name, s.Width, s.Height); } } }