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]);
  }
原文地址:https://www.cnblogs.com/notebook2011/p/2969630.html