Converting List using covariance

Yan Cui
theburningmonk.com
Published in
2 min readFeb 8, 2011

I saw an interesting use of covariance today, consider an interface IMyClass and an implementing class MyClass:

1: public interface IMyClass { }2:3: public class MyClass : IMyClass { }

If you want to convert an List<MyClass> to a List<IMyClass> you would normally use the Enumerable.Cast method but did you know that you can also use C# 4’s support for covariance in the type parameter 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 party trick best avoided for any production code, for which you should still prefer:

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

because:-

  • it achieves the same result
  • it is just as expressive
  • it is the standard way of doing this kind of conversions in LINQ
  • it is understood by most C# developers so unlikely to cause confusion

There’s another argument for using Cast, in the case of use-defined implicit/explicit operators. Imagine if you have another class which does not inherit from MyClass but defines an explicit operator which allows you to cast an instance of MyClass:

1: public class MyOtherClass2: {3:     public static explicit operator MyClass(MyOtherClass other)4:     {5:         return new MyClass();6:     }7: }

In cases like this, you won’t be able to use the covariance 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 anything8:     Console.WriteLine(original.Cast<IMyClass>().ToList().GetType());     // List<IMyClass>9:10:     // changes the compile type, works because of covariance11:     Console.WriteLine(original.ToList<IMyClass>().GetType());            // List<IMyClass>12:13:     // casts the objs to MyOtherClass using the defined convertor14:     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 inheritance18:     // relationship between MyClass and MyOtherClass19:     // Console.WriteLine(objs.ToList<MyOtherClass>().GetType());20: }

References:
StackOverflow question — Casting List<T> — covariance/contravariance problem

--

--

Yan Cui
theburningmonk.com

AWS Serverless Hero. Follow me to learn practical tips and best practices for AWS and Serverless.