• 泛型接口(协变和逆变)


      使用泛型可以定义接口,在接口中定义的方法可以带泛型参数。在链表的中,实现了IEnumerable<out T>接口,它定义了GetEnumerator()方法,返回IEnumerator<T>。.net中提供了许多泛型接口:IComparable<T>、ICollection<T>和IextensibleObject<T>等。同一个接口一般存在一个比较老的非泛型版本接口存在,如IComparable:

      在非泛型版本中,其参数是object类型(在.net中,object是一切类型的基础)。因此,在实现比较方法前,需要经过类型转换,才能使用:

    //非泛型版本比较接口
    public interface IComparable
    {
        int CompareTo(object obj);
    }
    
    // 非泛型实现比较接口
    class Person : IComparable
    {
        public int CompareTo(object obj)
        {
            Person other = obj is Person ? obj as Person : null;//比较前,必须要强制转换类型
            throw new NotImplementedException();
        }
    }

      在泛型版本中,则不需要经过转换类型,其实现方法时,会自动将当前类型作为参数的类型输入。

    public interface IComparable<in T>
    {     
        int CompareTo(T other);
    }
    //泛型实现比较接口
    class Person : IComparable<Person>
    {
        public int CompareTo(Person other)
        {
            throw new NotImplementedException();
        }
    }

    1、协变与逆变

      在.net 4.0之前,泛型接口时不变的。.net 4通过协变和逆变为泛型接口泛型委托添加了一个重要的扩展。协变和逆变是指对返回值和参数的类型进行转换。

      在.net中,函数参数类型是逆变的、返回类型是协变的。如有一个Shape类和Rectangle类,其中Shape是Rectangle类的父类。声明Display()方法是为了接受Shape类型的对象作为参数:public void Display(Shape s){ }。Display()方法可以传递派生自Shape基类的任意对象。因为Rectangle派生自Shape,所以下面的代码合法:

    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
    {
    }
    
    Rectangle r = new Rectangle { Width = 5, Height = 10 };
    Display(r);

      方法的返回类型是协变的。当方法返回一个Shape时,不能把它赋予Rectangle,因为Shape不一定是Rectangle的。但是,反过来是可行的。

    2、泛型接口的协变

      泛型接口的协变,需要使用out关键字标注。同时也意味着返回类型只能是T。接口IIndex与类型T是协变的,并从只读索引器中返回这个类型:

    public interface IIndex<out T>
    {
        T this[int index] { get; }
        int Count { get; }
    }

      使用RectangleCollection类实现接口IIndex<T>:

    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}
        };
    
        private static RectangleCollection coll;
        public static RectangleCollection GetRectangles()
        {
            return 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 { get { return data.Length; } }
    }

      方法GetReactangles()返回一个实现IIndex<Rectangle>接口的RectangleCollection类,因此可以把返回值赋予IIndex<Rectangle>类型的变量。因为接口是协变的,所以也可以把返回值赋值给IIndex<Shape>类型的变量。(因为没有这个IIndex<Shape>实现类型的类,但是Rectangele的父类是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]);
        }
    }

    3、泛型接口的逆变

      泛型接口的逆变需要关键字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>类型的变量。因为IDisplay<T>是逆变的,所以可以把结果赋予IDisplay<Rectangle>,Rectangle派生自Shape。接口的方法只能把泛型类型定义为输入:

    static void Main()
    {
        IIndex<Rectangle> rectangles = RectangleCollection.GetRectangles();
    
        IDisplay<Shape> shapeDisplay = new ShapeDisplay();
        IDisplay<Rectangle> rectangleDisplay = shapeDisplay;
        rectangleDisplay.Show(rectangles[0]);   
    }
  • 相关阅读:
    [ZJOI2008]树的统计 树链剖分
    CF915E 动态开线段树
    Poj 2114 Boatherds(点分治)
    Poj 2599 Godfather(树的重心)
    Bzoj 2152: 聪聪可可(点分治)
    Cogs 1714. [POJ1741][男人八题]树上的点对(点分治)
    Cogs 329. K- 联赛(最大流)
    Cogs 731. [网络流24题] 最长递增子序列(最大流)
    Bzoj 2282: [Sdoi2011]消防(二分答案)
    Cogs 732. [网络流24题] 试题库(二分图)
  • 原文地址:https://www.cnblogs.com/pilgrim/p/9240364.html
Copyright © 2020-2023  润新知