I'm currently reading Jon skeet's amazing book "C# in depth (3d version)". I'm stuck on p.99 dealing with lack of contravariance for generics.
class TestGenericVariance
{
public static void Main(string[] args)
{
IComparer<IShape> x =new AreaComparer();
List<Circle> circles = new List<Circle>();
circles.Add(new Circle());
circles.Add(new Circle());
//The following line is invalid, Sort expects IComparer<Circle>
circles.Sort(x);
}
public interface IShape
{
}
public class Circle : IShape
{
}
public class AreaComparer : IComparer<IShape>
{
public int Compare(IShape x, IShape y)
{
//No matter, just for the test
return 0;
}
}
}
This code is not working because Sort method expects
IComparer<Circle>
as parameter type.
One of the work around suggested p.99 is to make AreaComparer class generic :
class AreaComparer<T> : IComparer<T> where T : IShape
And then modify non-generic AreaComparer to derive from the generic one :
class AreaComparer : AreaComparer<IShape>
In C#2, the code doesn't compile because Sort method on circles is still expecting an
IComparer<Circle>
Am I missing something ?
Nota :If I change to :
class AreaComparer : AreaComparer<Circle>
The first line of code in the main method asks form an explicit conversion
Thank you all for your help.
The idea is that you make AreaComparer
generic like this:
public class AreaComparer<T> : IComparer<T> where T : IShape
{
public int Compare(T x, T y)
{
return x.Area.CompareTo(y.Area);
}
}
Then when you need to sort, you construct a comparer with the correct type argument:
circles.Sort(new AreaComparer<Circle>());
Note that this is assuming that IShape
has an Area
property (as stated at the bottom of p97). That isn't in your sample code, and is required in order to implement the comparer sensibly.
The part about the non-generic derived class was only to simplify cases where you're happy with using IShape
and don't want to keep repeating that.
See more on this question at Stackoverflow