c# 协变与抗变

时间:2023-03-10 01:00:37
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]);
}
}

相关文章