我对方差的经验很少,但是在阅读了相当多的信息之后我至少理解了基本概念(即方差描述了两种类型的关系和类似投射的这两种类型的关系)之间的关系。但是,我似乎无法理解将IComparable<T>
定义为逆变的意义或好处。乍一看,这实际上似乎阻碍了亚型之间的可比性。我希望有人能够对此事有所了解。
答案 0 :(得分:6)
我首先要处理IComparer<T>
- 你的问题中没有提到它,但是稍微容易“卖”然后导致IComparable<T>
。
假设您有三个类:
写AreaComparer : IComparer<Shape>
很容易。
Contravariance允许您按区域对List<Circle>
进行排序,因为IComparer<Shape>
(例如AreaComparer
)可转换为IComparer<Circle>
。
同样适用于IComparable<T>
- 如果Shape
本身使用IComparable<Shape>
宣布自己是Area
,那么您可以再次对List<Circle>
进行排序,因为每个圈子都是Circle
与自身相似的形状。
现在很多时候这实际上不会成为问题,因为你有从Shape
到Circle
的隐式转换。但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}}
有趣的是,类型推断不会在这里工作 - 但我不确定为什么。反过来本身就很好。