【CSharp】泛型接口的协变与抗变

协变与抗变

协变与抗变指对参数和返回值的类型进行转换

在.NET中,参数类型是协变的。假定有Shape和Rectangle类,Rectangle派生自Shape基类。声明Display()方法是为了接收Shape类型的对象作为其参数。

public void Display(Shape o)
{
    
}

现在可以传递派生自Shape基类的任意对象。因为Rectangle派生自Shape,所以Rectangle满足Shape的所有要求,编译器接收这个方法的调用。

var r = new Rectangle{Width = 5, Height = 2.5};
Display(r);

方法的返回类型是抗变的。当方法返回一个Shape是,不能把它赋予Rectangle,因为Shape不一定总是Rectangle。反过来是可行的:如果一个方法像GEtRectangle()方法那样返回一个Rectangle。

public Rectangle GetRectangle()
{
    
}

// 调用
Shape s = GetRectangle();

泛型接口的协变

如果泛型接口类型用out关键字标注,泛型接口就是协变的。这也意味着返回类型只能是T。接口IIndex与类型T是协变的,并从一个只读索引器中返回这个类型。

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

IIndex接口用RectangleCollection类来实现。RectangleCollection类为泛型类T定义了Rectangle。

public class RectangleCollection : IIndex
{
    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() => 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;

}

注意:如果对接口IIndex使用了读写所引器,就把泛型类型T传递给方法,并从方法中检索这个类型。这不能通过协变来实现——泛型乐星必须定义为不变的。不能用out和in标注,就可以把类型定义为不变的。

RectangleCollection.GetRectangle()方法返回一个实现IIndex接口的RectangleCollection类,所以可以把返回值赋予IIndex类型的变量rectangle。因为接口时协变的,所以可以把返回值赋予IIndex类型的变量。

泛型接口的抗变

如果泛型类型用in关键字标注,泛型接口就是抗变的。这样,接口只能把泛型类型T用作其方法的输入。

public interface IDisplay
{
    void Show(T item);
}

ShapeDisplay类实现IDisplay,并使用Shape对象作为输入参数。

public class ShapeDisplay : IDisplay
{
    public void Show(Shape s) => WriteLine($"{s.GetType().Name} Width: {s.Width}, Height: {s.Height}");
}

创建ShapeDisplay的一个新实例,会返回IDisplay,并把它赋予shapeDisplay变量。因为IDisplay是抗变的,所以可以把结果赋予IDisplay,其中Rectangle派生自Shape。这次接口的方法只能把泛型类型定义为输入,而Rectangle满足Shape的所有需求。

你可能感兴趣的:(CSharp)