c# 协变与抗变

定义

  1. 协变:与原始类型转换方向相同的可变性称为协变。
  2. 抗变:与派生类型转换方向相同的可变性称为抗变。

补充:

  1. 参数是协变的,可以使用派生类对象传入需要基类参数的方法,反之不行
  2. 返回值是抗变的,不能使用派生类对象接收返回了基类对象的方法返回值,反之可以

代码展示

public class 协变和抗变
{
    /// <summary>
    /// 基类
    /// </summary>
    public class Shape
    {
        public double Width { get; set; }
        public double Height { get; set; }
        public override string ToString() => $"width:{Width},height:{Height}";
    }
    /// <summary>
    /// 派生类
    /// </summary>
    public class Rect : Shape
    {

    }

    #region 协变接口

    /// <summary>
    /// 协变接口 --------------- 协变--》属性和索引器必须实现get
    /// </summary>
    public interface IIndex<out T>  // out声明接口为协变类型接口,继承了该接口的对象可以实现协变的隐式转换。  --对应调用方法中的shapes
    {
        T this[int index] { get; }
        int Count { get; }
    }

    /// <summary>
    /// 接口实现类
    /// </summary>
    public class RectCollection : IIndex<Rect>
    {
        private Rect[] data = new Rect[3] {
            new Rect{ Height=2,Width=5},
            new Rect{ Height=3,Width=7},
            new Rect{ Height=4.5,Width=2.9},
        };

        private static RectCollection _coll;
        public static RectCollection GetRect() => _coll ?? (_coll = new RectCollection());
        public Rect this[int index]
        {
            get
            {
                if (index < 0 || index > data.Length)
                    throw new ArgumentOutOfRangeException("index is out of range");
                return data[index];
            }
        }
        public int Count => data.Length;
    }

    #endregion

    #region 抗变接口

    /// <summary>
    /// 抗变接口  --------------- 抗变--》属性和索引器必须实现set
    /// </summary>
    public interface IDisplay<in T> // in声明接口为抗变类型接口,继承了该接口的对象可以实现抗变的隐式转换。  --对应调用方法中的rectDisplay
    {
        void Show (T item);
    }

    /// <summary>
    /// 抗变实现类
    /// </summary>
    public class ShapeDisplay : IDisplay<Shape>
    {
        public void Show(Shape item) => 
            Console.WriteLine($"{item.GetType().Name}  width:{item.Width}  height:{item.Height}");
    }

    #endregion

    static void Main()
    {
        // 协变调用  Rect-》Shape 向派生程度低的类装换
        IIndex<Rect> rects = RectCollection.GetRect();
        IIndex<Shape> shapes = rects;  // 如果IIndex接口的参数没有使用out修饰为协变,则转换报错(隐式转换会编译错误,显示转换会运行错误)
        for (int i = 0; i < shapes.Count; i++)
        {
            Console.WriteLine(shapes[i]);
        }

        // 抗变调用  Shape-》Rect 向派生程度高的类转换
        IDisplay<Shape> shapeDisplay = new ShapeDisplay();
        IDisplay<Rect> rectDisplay = shapeDisplay; // 如果IDisplay接口的参数没有使用in修饰为抗变,则转换报错
        rectDisplay.Show(rects[0]);
    }
}

猜你喜欢

转载自www.cnblogs.com/LTEF/p/10127607.html