The C# language defines an open type to be a type that's either a type argument or a generic type defined with unknown type arguments:
All types can be classified as either open types or closed types. An open type is a type that involves type parameters. More specifically:
- A type parameter defines an open type.
- An array type is an open type if and only if its element type is an open type.
- A constructed type is an open type if and only if one or more of its type arguments is an open type. A constructed nested type is an open type if and only if one or more of its type arguments or the type arguments of its containing type(s) is an open type.
A closed type is a type that is not an open type.
Therefore T
, List
, and Dictionary
, and Dictionary
are all open types (T
and U
are type arguments) whereas List
and Dictionary
are closed types.
There's a related concept: An unbound generic type is a generic type with unspecified type arguments. An unbound type can't be used in expressions other than typeof()
and you can't instantiate it or call its methods. For instance, List<>
and Dictionary<,>
are unbound types.
To clarify the subtle distinction between an open type and an unbound type:
class Program {
static void Main() { Test(); }
static void Test() {
Console.WriteLine(typeof(List)); // Print out the type name
}
}
If you run this snippet, it'll print out
System.Collections.Generic.List`1[System.Int32]
which is the CLR name for List
. It's clear at runtime that the type argument is System.Int32
. This makes List
a bound open type.
At runtime, you can use reflection to bind type arguments to unspecified type parameters of unbound generic types with the Type.MakeGenericType method:
Type unboundGenericList = typeof(List<>);
Type listOfInt = unboundGenericList.MakeGenericType(typeof(int));
if (listOfInt == typeof(List))
Console.WriteLine("Constructed a List type.");
You can check whether a type is an unbound generic type (generic type definition) from which you can construct bound types with the Type.IsGenericTypeDefinition property:
Console.WriteLine(typeof(Dictionary<,>).IsGenericTypeDefinition); // True
Console.WriteLine(typeof(Dictionary).IsGenericTypeDefinition); // False
To get the unbound type from a constructed type at runtime, you can use the Type.GetGenericTypeDefinition method.
Type listOfInt = typeof(List);
Type list = listOfInt.GetGenericTypeDefinition(); // == typeof(List<>)
Note that for a generic type, you can either have a completely unbound type definition, or a completely bound definition. You can't bind some type parameters and leave others unbound. For instance, you can't have Dictionary
or Dictionary<,string>
.