Generics allow you to define type-safe classes without compromising type safety, performance, or productivity. For example, when developing a general-purpose stack providing the classic Push() and Pop() methods, you would like to use it to store instances of various types.
public class Stack<T> { readonly int m_Size; int m_StackPointer = 0; T[] m_Items; public Stack():this(100) {} public Stack(int size) { m_Size = size; m_Items = new T[m_Size]; } public void Push(T item) { if(m_StackPointer >= m_Size) throw new StackOverflowException(); m_Items[m_StackPointer] = item; m_StackPointer++; } public T Pop() { m_StackPointer--; if(m_StackPointer >= 0) { return m_Items[m_StackPointer]; } else { m_StackPointer = 0; throw new InvalidOperationException("Cannot pop an empty stack"); } } }
The advantage of this programming model is that the internal algorithms and data manipulation remain the same while the actual data type can change based on the way the client uses your server code.
When you compile generic C# server-side code, the compiler compiles it into IL, just like any other type. However, the IL only contains parameters or place holders for the actual specific types. In addition, the metadata of the generic server contains generic information.