C## Generics in the Runtime
There is no difference between generic class and a regular class when a generic class is compiled. And the result of the compilation is metadata and intermediate language. This Intermediate Language (IL) is parameterized to accept a user-supplied type somewhere in code. How the IL for a generic type is used differs based on whether or not the supplied type parameter is a value or reference type.
The run-time creates a specialized generic type with the supplied parameter substituted in the appropriate places in the IL when a generic type is first constructed with a value type as a parameter. Specialized generic types are created once for each unique value type used as a parameter.
Suppose your program code declared a Stack constructed of integers:
Stack<int> stack;
Here, the run-time generates a specialized version of the Stack class with the integer substituted appropriately for its parameter. Now, whenever your program code uses a Stack of integers, the run-time reuses the generated specialized Stack class. In the following example, two instances of a Stack of integers are created, both using the code already generated by the run-time for a Stack of integers:
Stack<int> stackOne = new Stack<int>(); Stack<int> stackTwo = new Stack<int>();
If at another point in your program code another Stack class is created, this time with a different value type, such as a long or a user-defined structure as its parameter, the run-time generates another version of the generic type, this time substituting a long in the appropriate places in IL. The benefit of creating specialized classes for generics constructed with value types is better performance. After all, conversions are no longer necessary because each specialized generic class "natively" contains the value form.
The first time a generic type is constructed with any reference type, the run-time creates a specialized generic type with object references substituted for the parameters in the IL. Generics work slightly different for reference types. Then, each time a constructed type is instantiated with a reference type as its parameter, regardless of what type it is, the run-time reuses the previously created specialized version of the generic type.
Suppose you had two reference types, a Customer class and an Order class, and further suppose that you created a Stack of Customer types:
Stack<Customer> customers;
At this point, the run-time generates a specialized version of the Stack class that, instead of storing data, stores object references that will be filled in later. Suppose the next line of code creates a Stack of another reference type, called Order:
Stack<Order> orders = new Stack<Order>();
Another specialized version of the Stack class is not created for the Order type. Rather, an instance of the specialized version of the Stack class is created and the orders variable is set to reference it. For each object reference that was substituted in place of the type parameter, an area of memory the size of an Order type is allocated and the pointer is set to reference that memory location. Suppose you then encountered a line of code to create a Stack of a Customer type:
customers = new Stack<Customer>();
As with the previous use of the Stack class created with the Order type, another instance of the specialized Stack class is created, and the pointers contained therein are set to reference an area of memory the size of a Customer type. Because the number of reference types can vary wildly from program to program, the C# implementation of generics greatly reduces code bloat by reducing to one the number of specialized classes created by the compiler for generic classes of reference types.
When a generic C# class is instantiated with a type parameter, be it a value or reference type, it can be queried at runtime using reflection and both its actual type, as well as its type parameter can be established.
All the best!