Another gem today, when attempting to add a subclass collection to a base type list using the AddRange() method you may encounter the following error using this code:
List<BaseClassA> baseList = new List<BaseClassA>(); baseList.AddRange((IEnumerable<SubClassB>) subList); // Where 'SubClassB' is a class deriving from 'BaseClassA'
System.InvalidCastException was unhandled:
Message=”Unable to cast object of type ‘System.Collections.Generic.List`1[MyNamespace.BaseClassA]‘ to type ‘System.Collections.Generic.IEnumerable`1[MyNamespace.SubClassB]‘.”
Ah! You might think, well that's obvious, you are casting to IEnumerable when it's expecting List, well you would be wrong, because AddRange explicitly asks for an IEnumerable<T> type.
Well it turns out, we aren't even getting as far as evaulating the parameter given correctly, as "(IEnumerable<SubClassB>) subList" is actually failing before anything else.
If we QuickWatch that by itself, we can see a very similar error:
Cannot cast 'SubClassB' (which has an actual type of 'System.Collections.Generic.List<SubClassB>') to 'System.Collections.Generic.IEnumerable<BaseClassA>'
So what could be going on?
Well from what I have read, apparently .NET does not [yet] support covariance, which I believe is what is stopping us from achieving our goal above, and the word on the street is we might expect to see it in .NET 4.0.
Now there is a way around this, not all is lost if we didn't have an answer (luckily I do below). To illustrate just how annoying this error is, we can do a bit more hard work for the compiler and achieve a populated list successfully full of SubClassA's...
foreach (var subClass in subList)
{
baseList.Add(subClass);
}
That works perfectly:
baseList:
Count = 2
[0]: {MyNamespace.SubClassA}
[1]: {MyNamespace.SubClassA}
So with that said, and a long-way-around demonstrated, there has to be an easier way that IS available right now? Well yep, now LINQ has came along in .NET 3.0+ we can use the extension method Cast<T>... Let's check it out.
baseList.AddRange(subList.Cast<BaseClassA>());
That's it! You will receive a populated list just like the one done with individual Add's in the previous example!
Post a Comment