3/10/13

Generic Contravariance in C#

In my previous post .NET interfaces Part 3 I had written about the IEqualityComparer interface which has the following signature:

 public interface IEqualityComparer<in T>
 {
        bool Equals(T x, T y);        
        int GetHashCode(T obj);
 }
 
The keyword "in" before the type T indicates that this interface is contravariant.

In the same post we were calling the Contains method on a collection of type FootballStar as shown below:

FootballStars.Contains(Peyton2,new StarComparer())
We were passing in an instance of type IEqualityComparer<Star> to a method that was expecting an IEqualityComparer<FootballStar> i.e we were able to pass a less derived type than was specified by the type parameter.

This was possible because the interface IEqualityComparer<in T> is a contravariant interface that allows us to pass a less derived type than the specified type parameter.

Even though we are passing in a less derived type, everything works perfectly because the instances that ultimately get passed to the Equals method of the IEqualityComparer method are still of type FootballStar(since the collection is of type FootballStar), which derives from Star.

If the interface was not Contravariant, we would have to create a new IEqualityComparer of type FootballStar and pass that to the contains method. The compiler will not have it any other way. We would also have to repeat this for every type that derived from Star, assuming that all instances of type Star(e.g. FootballStar, BaseballStar) wish to use the same logic to determine the equality of their instances.

We would end up writing code like this:

//a comparer for FootballStar instances
public class FootballStarComparer:IEqualityComparer<FootballStar>
{
}
//that will be passed to contains FootballStars.Contains(Peyton2,new FootballStarComparer())

//a comparer for BaseballStar instances public class BaseballStarComparer:IEqualityComparer<BaseballStar> { }
//that will be passed to contains BaseballStars.Contains(bbStar1,new BaseballStarComparer())
So instead of using the type inheritance heirarchy and polymorphism we would be writing a lot of repititive code to perform similar tasks.

No comments: