4

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

4

1 回答 1

7

我将IComparer<T>首先解决 - 你的问题中没有提到它,但它是一个稍微容易一点的“销售”,然后导致IComparable<T>.

假设你有三个类:

  • 形状(具有区域属性)
  • 圆:形状
  • 正方形

写一个很容易AreaComparer : IComparer<Shape>

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

同样 for IComparable<T>- 如果Shape它自己声明自己正在IComparable<Shape>使用Area,那么您可以再次对 a 进行排序,List<Circle>因为每个圆都可以与它自己作为一个形状进行比较。

现在很多时候这实际上并不是一个问题,因为你会有从Circleto的隐式转换ShapeCircle但是在泛型方法的类型推断方面,将 a视为 an的自然能力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,我建议 - 这仅在 aCircleIComparable<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);
    }
}

有趣的是,类型推断在这里不起作用——但我不确定为什么。不过,逆变本身很好。

于 2012-08-03T12:30:53.773 回答