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.
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.