c# 协变与抗变

定义

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

补充:

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

代码展示

public class 协变和抗变
{
///


/// 基类
///

public class Shape
{
public double Width { get; set; }
public double Height { get; set; }
public override string ToString() => $"width:{Width},height:{Height}";
}
///
/// 派生类
///

public class Rect : Shape
{

}

region 协变接口

///


/// 协变接口 --------------- 协变--》属性和索引器必须实现get
///

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

///


/// 接口实现类
///

public class RectCollection : IIndex
{
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 抗变接口

///


/// 抗变接口 --------------- 抗变--》属性和索引器必须实现set
///

public interface IDisplay // in声明接口为抗变类型接口,继承了该接口的对象可以实现抗变的隐式转换。 --对应调用方法中的rectDisplay
{
void Show (T item);
}

///


/// 抗变实现类
///

public class ShapeDisplay : IDisplay
{
public void Show(Shape item) =>
Console.WriteLine($"{item.GetType().Name} width:{item.Width} height:{item.Height}");
}

endregion

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

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

posted @ 2018-12-16 19:11  Super-Yan  阅读(562)  评论(0)    收藏  举报