C#中協變與抗變(逆變)
阿新 • • 發佈:2018-10-31
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; }