Converting List<T> using covariance

I saw an inter­est­ing use of covari­ance today, con­sid­er an inter­face IMy­Class and an imple­ment­ing class MyClass:

   1: public interface IMyClass { }

   2:

   3: public class MyClass : IMyClass { }

If you want to con­vert an List<MyClass> to a List<IMyClass> you would nor­mal­ly use the Enumerable.Cast method but did you know that you can also use C# 4’s sup­port for covari­ance in the type para­me­ter and do this instead:

   1: var original = new List<MyClass>();

   2:

   3: var converted = original.ToList<IMyClass>()

Funky, eh? ;-)

Though I think it’s a par­ty trick best avoid­ed for any pro­duc­tion code, for which you should still pre­fer:

   1: var converted = original.Cast<IMyClass>().ToList();

because:-

  • it achieves the same result
  • it is just as expres­sive
  • it is the stan­dard way of doing this kind of con­ver­sions in LINQ
  • it is under­stood by most C# devel­op­ers so unlike­ly to cause con­fu­sion

There’s anoth­er argu­ment for using Cast, in the case of use-defined implicit/explicit oper­a­tors. Imag­ine if you have anoth­er class which does not inher­it from MyClass but defines an explic­it oper­a­tor which allows you to cast an instance of MyClass:

   1: public class MyOtherClass

   2: {

   3:     public static explicit operator MyClass(MyOtherClass other)

   4:     {

   5:         return new MyClass();

   6:     }

   7: }

In cas­es like this, you won’t be able to use the covari­ance trick:

   1: void Main()

   2: {

   3:     var original = new List<MyClass>();

   4:

   5:     Console.WriteLine(original.GetType());                               // List<MyClass>

   6:

   7:     // cast here doesn't actually do anything

   8:     Console.WriteLine(original.Cast<IMyClass>().ToList().GetType());     // List<IMyClass>

   9:

  10:     // changes the compile type, works because of covariance

  11:     Console.WriteLine(original.ToList<IMyClass>().GetType());            // List<IMyClass>

  12:

  13:     // casts the objs to MyOtherClass using the defined convertor

  14:     Console.WriteLine(original.Cast<MyOtherClass>().ToList().GetType()); // List<MyOtherClass>

  15:

  16:     // this line won't compile.

  17:     // it doesn't work because this is not covariance, there's no inheritance

  18:     // relationship between MyClass and MyOtherClass

  19:     // Console.WriteLine(objs.ToList<MyOtherClass>().GetType());

  20: }

References:

Stack­Over­flow ques­tion – Cast­ing List<T> – covariance/contravariance prob­lem