c#协变和抗变_C#的协变和抗变

一、协变和抗变的基本概念

在.NET4之前,泛型接口是不变的。.NET4通过协变和抗变为泛型接口和泛型委托添加了一个重要的扩展。协变和抗变指对参数和返回值的类型进行转换。例如,可以给一个需要Shape参数的方法传入一个Rectangle参数吗?下面用实例说明这些扩展的优点。

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

public void Display(Shape o) { }

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

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

Display(r);

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

public Rectangle GetRectangle();

就可以把结果赋予某个Shape:

Shape s = GetRectangle();

在.NET Framework 4版本之前,这种行为方式不适用于泛型。在C#4中,扩展后的语言支持泛型接口和泛型委托的协变和抗变。下面开始定义Shape基类和Rectangle类:

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.ToString(), Height.ToString());

}

}

public class Rectangle : Shape

{

}

二、泛型接口的协变

如果泛型类型用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 readonly Rectangle[] _data = new Rectangle[3]

{

new Rectangle{Height = 2,Width = 5},

new Rectangle{Height = 3,Width = 4},

new Rectangle{Height = 4,Width = 5},

};

public static RectangleCollection GetRectangles()

{

return new RectangleCollection();

}

public Rectangle this[int index]

{

get

{

if (index  _data.Length)

throw new ArgumentOutOfRangeException("index");

return _data[index];

}

}

public int Count { get { return _data.Length; } }

}

RectangleCollection.GetRectangles()方法返回一个实现IIndex接口的RectangleCollection类,所以可以把返回值赋予IIndex类型中的变量rectangle。因为接口是协变的,所以也可以把返回值赋予IIndex类型的变量。Shape不需要Rectangle没有提供的内容。使用shapes变量,就可以在for循环中使用接口中的索引器和Count属性:

static void Main(string[] args)

{

//协变

IIndex rectangles = RectangleCollection.GetRectangles();

IIndex shapes = rectangles;

for (var i = 0; i 

{

Console.WriteLine(shapes[i]);

}

}

运行结果如下:

三、泛型接口的抗变

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

public interface IDisplay

{

void Show(T s);

}

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

public class ShapeDisplay : IDisplay

{

public void Show(Shape s)

{

Console.WriteLine("{0} Width: {1}, Height: {2}.", s.GetType().Name, s.Width, s.Height);

}

}

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

private static void Main(string[] args)

{

//协变

IIndex rectangles = RectangleCollection.GetRectangles();

IIndex shapes = rectangles;

for (var i = 0; i 

{

Console.WriteLine(shapes[i]);

}

//抗变

IDisplay shapeDisplay = new ShapeDisplay();

IDisplay rectangleDisplay = shapeDisplay;

rectangleDisplay.Show(rectangles[0]);

}

运行结果如下:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值