.NET Generics and Type Inference Landmine
May 21, 2008 4:57 pm programmingLet’s say that you have a class that takes a generic type parameter:
public class Foo<T> {
public void accept(T someObject) { }
public void doSomething() { }
}
It’s difficult to doSomething() to a collection of Foo<T>s when you don’t know what T is and/or there’s multiple types used for T. To work around this, you can create a typeless interface:
public interface FooTypeless {
void doSomething();
}
The declaration of Foo<T> now becomes:
public class Foo<T> : FooTypeless
Now you can have this method:
public void doSomethingOnAllFoos(
IEnumerable<FooTypeless> foos) {
foreach( FooTypeless foo in foos ) {
foo.doSomething();
}
}
Also, say that you have the following extension method:
public static IEnumerable<T> toEnumerable<T>(this <T> obj ) {
// Any IEnumerable implementation should work here;
// but we'll get to that in a bit
return new T[] { obj };
}
Type inference would let you write this:
FooaFoo = new Foo (); doSomethingOnAllFoos( aFoo.toEnumerable() );
However, this code does not work; you get a compile error:
cannot convert from ‘System.Collections.Generic.IEnumerable<Foo<string>>’ to ‘System.Collections.Generic.IEnumerable<FooTypeless>’
This occurs even though Foo<string> inherits from FooTypeless. Even though the generic type parameters are compatible, the generic-enabled reference isn’t, at least according to the compiler. (We humans could probably see that IEnumerable is an interface that could safely be converted, but the existing compiler cannot).
Now, you can do this:
IEnumerable<FooTypeless> someFoos
= new Foo<String>[] { foo };
…but not this:
IEnumerable<Foo<String>> someStringFoos
= new Foo<String>[] { foo };
IEnumerable<FooTypeless> someFoos
= someStringFoos;
You may be tempted to try casting to work around this. For example, this works:
IEnumerable<Foo<String>> someStringFoos
= new Foo<String>[] { foo };
IEnumerable<FooTypeless> someFoos
= (IEnumerable<FooTypeless>) someStringFoos;
However, this only works if someStringFoos is assigned an array. The following does not work:
IEnumerable<Foo<String>> someStringFoos
= new List<Foo<String>> { foo };
IEnumerable<FooTypeless> someFoos
= (IEnumerable<FooTypeless>) someStringFoos;
If you try this, you’ll get an InvalidCastException. Furthermore, this generates the compiler error:
IEnumerable<FooTypeless> someFoos
= new List<Foo<String>> { foo };
Currently I have no idea why the array works but the List doesn’t; it doesn’t make a lot of sense to me.
I originally discovered this by using type inference with my toEnumerable() extension method. An easy way around these problems is to avoid the type inference completely and explicitly specify the type of IEnumerable you want:
Foo<String> aFoo = new Foo<String>(); doSomethingOnAllFoos( aFoo.toEnumerable<FooTypeless>() );
This works fine, even if toEnumerable() uses a non-array type (such as List) as its IEnumerable implementation. Execution-wise, nothing has changed, but the generic type used in the call can make or break the code.
Stuff like this starts to make Java’s type erasure look not so ugly in comparison. ![]()

May 21st, 2008 at 7:36 pm
Nice jab at the end
BTW you’re missing some semicolons in your code snippets for greater-than signs.
May 21st, 2008 at 8:11 pm
Fixed, thanks for pointing it out.
May 23rd, 2008 at 6:49 am
The reason why the array works and generics don’t is because they wanted to avoid this, which is possible with arrays but not generic lists:
string[] strArray = new string[5]…
object[] objArray = strArray; //legal; arrays are covariant
objArray[0] = 5; //runtime error
May 23rd, 2008 at 9:03 am
Makes sense, but then why not just disallow the casting of arrays? They already disallow casting of typed lists.
I’m guessing that the answer is “because Java allowed that”, and MS just followed suit (which is a running theme with .NET).