Benefits of IComparable <T> as a contravariant?

I have very little experience with dispersion, but after reading a little, I understand that I understand at least the basic concepts (that is, variance describes the relationship between the relations of two types and the ratio of these two types, similarly designed). However, I cannot understand the meaning or benefits of IComparable<T> defined as contravariant. At first glance, this actually hinders comparability between subtypes. I hope someone can shed light on this.

+4
source share
1 answer

Iโ€™ll IComparer<T> - this is not mentioned in your question, but it is a bit easier to โ€œsellโ€, which then leads to IComparable<T> .

Suppose you have three classes:

  • Shape (has Area property)
  • Circle: Shape
  • Square: Shape

It is easy to write AreaComparer : IComparer<Shape> .

Contravariance allows you to sort by List<Circle> by region, because IComparer<Shape> (e.g. AreaComparer ) is converted to IComparer<Circle> .

Similarly for IComparable<T> - if Shape declared itself IComparable<Shape> using Area , then again you could sort a List<Circle> , because each circle would be comparable to itself as a shape.

Now, a lot of time is actually not a problem, because you will have an implicit conversion from Circle to Shape . But the natural ability of a Circle be seen as IComparable<Circle> can help in terms of type inference for common methods. For example, suppose we have:

 void Foo<T>(IComparable<T> item1, T item2) 

And we are trying to call

 Foo(circle1, circle2); 

I don't know if the compiler (without contravariance) can be able to output T=Shape , which will work ... but even if it could, it would fail for:

 void Foo<T>(IComparable<T> item1, T item2) where T : ISomethingCircleImplements 

Indeed, we want the compiler to be happy with T=Circle , I would suggest - this is only valid if Circle is IComparable<Circle> through covariance.

EDIT: Here's an example of work:

 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); } } 

Interestingly, type inference doesn't work here, but I'm not sure why. However, contraception itself is good.

+6
source

All Articles