IComparable <t>作为逆变的好处?</t>

时间:2012-08-03 12:26:03

标签: c# contravariance icomparable

我对方差的经验很少,但是在阅读了相当多的信息之后我至少理解了基本概念(即方差描述了两种类型的关系和类似投射的这两种类型的关系)之间的关系。但是,我似乎无法理解将IComparable<T>定义为逆变的意义或好处。乍一看,这实际上似乎阻碍了亚型之间的可比性。我希望有人能够对此事有所了解。

1 个答案:

答案 0 :(得分:6)

我首先要处理IComparer<T> - 你的问题中没有提到它,但是稍微容易“卖”然后导致IComparable<T>

假设您有三个类:

  • Shape(具有Area属性)
  • 圆形:形状
  • 方形:形状

AreaComparer : IComparer<Shape>很容易。

Contravariance允许您按区域对List<Circle>进行排序,因为IComparer<Shape>(例如AreaComparer)可转换为IComparer<Circle>

同样适用于IComparable<T> - 如果Shape本身使用IComparable<Shape>宣布自己是Area,那么您可以再次对List<Circle>进行排序,因为每个圈子都是Circle与自身相似的形状。

现在很多时候这实际上不会成为问题,因为你有从ShapeCircle的隐式转换。但IComparable<Circle>被视为void Foo<T>(IComparable<T> item1, T item2) 的自然能力可能有助于通用方法的类型推断。例如,假设我们有:

Foo(circle1, circle2);

我们试着打电话

T=Shape

我不知道编译器是否会(没有逆变)能够推断void Foo<T>(IComparable<T> item1, T item2) where T : ISomethingCircleImplements ,这会起作用......但即使可以,也会失败:

T=Circle

我们真的希望编译器对Circle感到满意,我建议 - 只有当IComparable<Circle>通过协方差using System; public abstract class Shape : IComparable<Shape> { public abstract double Area { get; } public int CompareTo(Shape other) { return Area.CompareTo(other.Area); } } public interface ISomethingCircleImplements {} public class Circle : Shape, ISomethingCircleImplements { private readonly double radius; public Circle(double radius) { this.radius = radius; } public override double Area { get { return radius * radius * Math.PI; } } } class Test { static void Foo<T>(IComparable<T> item1, T item2) where T : ISomethingCircleImplements { Console.WriteLine(item1.CompareTo(item2)); } static void Main() { Circle c1 = new Circle(10); Circle c2 = new Circle(20); Foo<Circle>(c1, c2); } } 时才有效。

编辑:这是一个有效的例子:

{{1}}

有趣的是,类型推断不会在这里工作 - 但我不确定为什么。反过来本身就很好。