C#中协变与抗变(逆变)
在.NET 4之前,泛型接口是不变的,.NET4 通过协变 和抗变为泛型接口和泛型委托添加了一个重要的扩展。
协变和抗变指对参数和返回值的类型进行转换
在.NET 中 参数类型是协变的;
也就是父类可以包含子类。
Cube 继承于 Shape类;
pulic class Cube:Shape
{
}
public class Shape
{
public double width{get;set;}
public double heigth{get;set;}
}
public class Test
{
static void Main()
{
Cube a = new Cube();
Shape b = new Shape();
b=a;
Display(a);
}
public static void Display(Shape o)
{
//do something
}
}
这里 用父类作为参数, 因为 cube 派生自Shap类,,满足Shap的所有要求,编译器接受这个方法的调用,
而且父类可以作为容器 来储存 子类;但本质上是子类不可以去储存父类; 但是也可以强制转换下让编译器去通过
方法的返回类型是抗变的
泛型类型用 out关键字 标注,泛型接口就是协变的。返回类型只能是T。
public interface IIndex<out T>
{
T this [int index]{get;}
int Count{get;}
}
接口IIndex 和类型T的协变的,并从一个只读索引器重返回这个类型
在 .NET4.0后 扩展的语言支持 泛型接口和泛型委托的协变和抗变.
不使用 in out标注,泛型就是不变的
泛型接口的抗变
如果泛型类型用in 关键字标注,泛型接口就是抗变的。这样 接口只能把泛型类型T用作为其方法的输入
public interface IDisplay<in T>
{
void Show(T item);
}
public class ShapeDisplay : IDisplay<Shap>
{
public void Show(Shap shap) => Console.WriteLine($"{shap.GetType().Name} width:{shap.Width},height:{shap.Height}");
}
public static void Main()
{
// 6.42 泛型接口的协变;
IIndex<Rectangle> rectangles = RectangleCollection.GetRectangles();
IIndex<Shap> shapes = rectangles;
for (int i = 0; i < shapes.Count; i++)
{
Console.WriteLine(shapes[i]);
}
Console.WriteLine(shapes[1]);
//抗变;
IDisplay<Shap> shapDisplay = new ShapeDisplay();
IDisplay<Rectangle> rectangleDisplay = shapDisplay;
//创建一个 ShapDisplay的新实例 会返回IDisplay<Shap> 并把它赋予shapeDisplay变量.
//因为IDisplay<T>是抗变的。所以可以把结果 赋予IDisplay<Rectangle> ,Rectangle类继承Shap类
//这次接口的方法只能 把泛型类型定义为输入,而Rectangle满足Shape的所有要求
rectangleDisplay.Show(rectangles[0]);
}
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=2,Width=5},
};
private static RectangleCollection _coll;
public static RectangleCollection GetRectangles() => _coll ?? (_coll = new RectangleCollection());
public Rectangle this[int index]
{
get
{
if (index < 0 || index > data.Length)
throw new ArgumentOutOfRangeException("index");
return data[index];
}
}
public int Count => data.Length;
}