Sunday, March 14, 2010

"Value types do not support variance".... but why?

I was reading over the details regarding the new way the IEnumerable<out T> interface is implemented in C# 4.0, specifically as it relates to covariance and contravariance. In my reading, I came across this interesting statement:
"Value types do not support variance."
What does that mean?

Certainly, the following code compiles just fine:
IEnumerable<object> strings = new List<string>();
But the following does not:
IEnumerable<object> ints = new List<int>();
Nothing more is stated in the documentation, other than that variance is not supported on value types. So I got to thinking... why is this?

Here's my theory (and I'd love to have someone from Microsoft verify this for me). When you're converting the List<string> to an IEnumerable<object>, you're not actually changing the real essence of what your original List<string> is. Internally, the List<string> class is going to still hold on to an array of references to objects on the heap, but instead of specifying that those objects on the heap are strings, we simply say they're objects instead. An array of value types, however, is different. The array of integers is a block in memory that holds the actual values, whereas the array of strings is a block of memory that holds references to the actual values, which are residing somewhere else on the heap. So what would have to happen in order for the covariance to work? Basically, an entirely new list would have to be created, and each value that resides in the internal array supporting List<int> would have to be boxed and the boxed reference placed in a new array, thus creating an entirely new List<object> to support this kind of thing.

This would be more than simply a performance hit. This would require every implementor of IEnumerable<out T> to define an "escape route" so to speak for when the type of T is a value type. So, instead of jumping through hoops to support this scenario, it's simply disallowed.