Inside .NET - Partially Constructed Generic Types

Given these class:

class B<T, U> {
}
 
class D : B<int, string> {
}
 
class E<T> : B<T, string> {
}

The result of:

typeof(B<,>)

Is a generic type definition, which can be used to make a fully constructed type using Type.MakeGenericType(typeT,typeU).

The result of:

typeof(D).BaseType

Is a fully constructed generic type: B<int,string>

But what's the result of:

typeof(E<>).BaseType

It's a half-constructed type: B<T, string>

Obviously, you can't instantiate an object of this type, as T is undefined; but you might expect that you could call MakeGenericType(typeT) with a single parameter, which will make a fully constructed type. But you can't.

Type has two properties that together tell you what kind of generic type you've got:

  • Type.IsGenericTypeDefinition
  • Type.ContainsGenericParameters

You can only make a fully constructed type from a type where IsGenericTypeDefinition is true, and you can only instantiate a class where ContainsGenericParameters is false.

Which leaves the middle ground, which is inhabited by these half-constructed types, where IsGenericTypeDefinition is false, but ContainsGenericParameters is true, so they can't be instantiated, and they can't be used to make fully-constructed generic types.

Although you can call GetGenericTypeDefinition(), which returns what you would expect, and you can also call GetGenericArguments(), which when called on type B<T, string> returns the second type argument as string, as expected, but the first type argument is returned as T, with its Type.IsGenericParameter property set to true. The base class of this type T is the base-class constraint of the type parameter (note that this does not include any interface constraints).

One question this leaves me with is why I can't call MakeGenericType() on a half-constructed generic type, if I provide the missing type argument(s)...

Leave a Reply