Generic is a class which allows the user to define classes and methods with the placeholder. Generics were added to version 2.0 of the C# language. The basic idea behind using Generic is to allow type (Integer, String, … etc and user-defined types) to be a parameter to methods, classes, and interfaces.
A generic type is a generic class or interface that is parameterized over types. The following Box class will be modified to demonstrate the concept.
An unbound type refers to the entity declared by a type declaration. An unbound generic type is not itself a type, and cannot be used as the type of a variable, argument or return value, or as a base type. The only construct in which an unbound generic type can be referenced is the typeof expression (§11.7. 16).
From the point of view of reflection, the difference between a generic type and an ordinary type is that a generic type has associated with it a set of type parameters (if it is a generic type definition) or type arguments (if it is a constructed type). A generic method differs from an ordinary method in the same way.
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<T>
, and Dictionary<string,T>
, and Dictionary<T,U>
are all open types (T
and U
are type arguments) whereas List<int>
and Dictionary<string,int>
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<int>(); }
static void Test<T>() {
Console.WriteLine(typeof(List<T>)); // 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<int>
. It's clear at runtime that the type argument is System.Int32
. This makes List<T>
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<int>))
Console.WriteLine("Constructed a List<int> 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<int,int>).IsGenericTypeDefinition); // False
To get the unbound type from a constructed type at runtime, you can use the Type.GetGenericTypeDefinition
method.
Type listOfInt = typeof(List<int>);
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<int,>
or Dictionary<,string>
.
An "open generic type" is just a generic type that doesn't yet have its type specified (e.g., CargoCrate<T>
). It becomes "closed" once a concrete type has been assigned (e.g. CargoCrate<Widget>
).
For example, say you have something like this:
public class Basket<T> {
T[] basketItems;
}
public class PicnicBlanket<T> {
Basket<T> picnicBasket; // Open type here. We don't know what T is.
}
// Closed type here: T is Food.
public class ParkPicnicBlanket : PicnicBlanket<Food> {
}
Here, picnicBasket
's type is open: nothing's yet been assigned to T
. When you make a concrete PicnicBlanket with a specific type -- for example, by writing PicnicBlanket<Food> p = new PicnicBlanket<Food>()
-- we now call it closed.
Just to add:
Dictionary<string, T>
(or more precisely Dictionary<string,>
) is still an open type.
Example:
void Foo<T>(Dictionary<string,T> dic) { ... }
There are three kinds of generic types. To make it short, in this (simplified) declaration:
public class Dictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
Dictionary<TKey, TValue>
is an unbounded generic type.
KeyValuePair<TKey, TValue>
is, in this case, an open constructed generic type. It has some type parameters, but they are already defined elsewhere (in Dictionary, in this case).
Dictionary<string, int>
would be a closed constructed generic type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With