C# 高级编程(笔记3)
第5章 泛型
1.泛型类的静态成员
泛型类的静态成员只能在类的一个实例中共享
public class staticDemo<T> { public static int x; }
由于同时对一个string类型和一个int类型使用了staticDemo<T>类,所以存在两组静态字段:
staticDemo<string>.x = 4; staticDemo<int>.x = 5; Console.WriteLine(staticDemo<string>.x); //输出 4
2.协变和抗变:指对参数和返回值的类型进行转换
假定有Shape和Rectangle类,Rectangle派生自Shape基类。声明Display()方法是为了接受Shape类型的对象作为其参数:
(1)
public void Display(Shape o) { }
----现在可以传递派生自Shape基类的任意对象。因为Rectangle派生自Shape基类,所以Rectangle满足Shape的所有要求,编译器接受这个方法的调用:
Rectangle r = new Rectangle { Width= 5, Height=2.5};
Display(r);//子类转父类,协变的作用
(2)
当方法返回一个Shape时,不能把它赋予Rectangle,因为Shape不一定总是Rectangle。反过来是可行的:
public Rectangle GetRectangle();
Shape s = GetRectangle();
对于上面的(1)(2)行为,在.net4版本之前,这种行为方式不适用于泛型。但在.net4中,扩展后的语言支持泛型接口和泛型委托的协变和抗变。
也就是可以这样:ITest<Shape> shape = ITest<Rectangle> rectangle;//协变
A.协变:子类向父类方向转变 ----------------- 参数类型是协变的。
B.抗变:父类向子类方向转变,不一定能转成功 ----------------- 方法的返回类型是抗变的。
3.泛型接口的协变
如果泛型类型用 out 关键字标注,泛型接口就是协变的。这也意味着返回类型只能是T。下面的IIndex与类型T是协变的,并从一个只读索引器返回这个类型
public interface IIndex<out T> { T this[int index]{get;} int Count{get;} }
下面用RectangleCollection类来实现接口IIndex<out T>,RectangleCollection类为泛型类型T定义了Rectangle:
public class RectangleCollection: IIndex< Rectangle > { public static 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; } } }
----由上面可以看出,RectangleCollection.GetRectangles()返回一个实现IIndex< Rectangle >接口的RectangleCollection类,所以可以把返回值赋予IIndex<Rectangle>类型的变量rectangle。
因为接口是协变的,所以也可以把返回值赋予IIndex<Shape>类型的变量。【Rectangle继承于Shape】
static void Main() { IIndex<Rectangle> rectangles = RectangleCollection.GetRectangles(); IIndex<Shape> shapes = rectangles; for (int i = 0; i < shapes.Count; i++) { Console.WriteLine(shapes[i]); } }
4.泛型接口的抗变
如果泛型类型用 in 关键字标注,泛型接口就是抗变的。这样,接口只能把泛型类型T用作其方法的参数类型,而不能用作其方法的返回值类型
public interface IDisplay<in T> { void Show(T item); } ShapeDisplay类实现IDisplay<Shape>,并使用Shape对象作为输入参数: public class ShapeDisplay: IDisplay<Shape> { public void Show(Shape s) { Console.WriteLine("{0} Width: {1}, Height: {2}", s.GetType().Name, s.Width, s.Height); } } ----创建ShapeDisplay的一个新实例,会返回IDisplay<Shape>,并把它赋予shapeDisplay变量。因为IDisplay<T>是抗变的,所以可以把结果赋予IDisplay<Rectangle>,其中Rectangle类派生自Shape。 static void Main() { //... IDisplay<Shape> s = new ShapeDisplay(); IDisplay<Rectangle> r = s;//抗变,父类向子类方向转变 rectangleDisplay.Show(RectangleCollection.data[0]); }