Generics are added in the C# language and common language runtime (CLR) version 2.0. Generics introduce the concept of type parameters into the .NET Framework, which makes it possible to design classes and methods: These classes and methods delay the specification of one or more types until the client code declares and instantiates the class or method. For example, by using the generic type parameter T, you can write a single class that other client code can use without introducing the cost or risk of runtime casting or boxing operations, as shown below:
// Declare the generic class. public class GenericList<T> { void Add(T input) { } } class TestGenericList { private class ExampleClass { } static void Main() { // Declare a list of type int. GenericList<int> list1 = new GenericList<int>(); // Declare a list of type string. GenericList<string> list2 = new GenericList<string>(); // Declare a list of type ExampleClass. GenericList<ExampleClass> list3 = new GenericList<ExampleClass>(); } }
Generic Overview
Using generic types allows you to maximize code reuse, protect type security, and improve performance.
The most common use of generics is to create collection classes.
The .NET Framework class library contains several new generic collection classes in the namespace. These classes should be used as much as possible instead of ordinary classes, such as ArrayList in namespaces.
You can create your own generic interfaces, generic classes, generic methods, generic events, and generic delegates.
Methods that can be constrained to access specific data types.
Information about the types used in generic data types can be obtained at runtime by using reflection.
Generic classes and generic methods are both reusable, type-safe and efficient, which is not available to non-generic classes and non-generic methods. Generics are usually used together with sets and methods acting on sets. The .NET Framework version 2.0 class library provides a new namespace containing several new generic-based collection classes. It is recommended that all applications targeting .NET Framework 2.0 and later use new generic collection classes instead of old non-generic collection classes such as ArrayList.
Of course, you can also create custom generic types and methods to provide your own universal solution to design efficient patterns for type-safety. The following code example demonstrates a simple generic link list class for demonstration purposes. (In most cases, the List<T> class provided by the .NET Framework class library should be used instead of creating the class yourself.) In cases where concrete types are usually used to indicate the type of items stored in the list, the type parameter T can be used. Its usage is as follows:
Type as method parameter in the AddHead method.
Return type as public methods GetNext and Data properties in Node nested classes.
Types of data as private member in nested classes.
Note that T can be used for Node nested classes. If GenericList<T> is instantiated with a concrete type (for example, as GenericList<int>), all Ts will be replaced with int.
// type parameter T in angle brackets public class GenericList<T> { // The nested class is also generic on T. private class Node { // T used in non-generic constructor. public Node(T t) { next = null; data = t; } private Node next; public Node Next { get { return next; } set { next = value; } } // T as private member data type. private T data; // T as return type of property. public T Data { get { return data; } set { data = value; } } } private Node head; // constructor public GenericList() { head = null; } // T as method parameter type: public void AddHead(T t) { Node n = new Node(t); = head; head = n; } public IEnumerator<T> GetEnumerator() { Node current = head; while (current != null) { yield return ; current = ; } } }
The following code example demonstrates how client code uses the generic GenericList<T> class to create an integer list. Just change the type parameters to easily modify the following code example to create a list of strings or any other custom types:
class TestGenericList { static void Main() { // int is the type argument GenericList<int> list = new GenericList<int>(); for (int x = 0; x < 10; x++) { (x); } foreach (int i in list) { (i + " "); } ("\nDone"); } }
Advantages of generics
In the common language runtime and in earlier versions of C#, generalization was achieved by casting between a type and a common base type Object, and generics provide solutions to this limitation. By creating generic classes, you can create a type-safe collection at compile time.
The limitations of using non-generic collection classes can be demonstrated by writing a small program that uses the ArrayList collection class from the .NET Framework class library. ArrayList is a very convenient collection class that can be used to store any reference or value types without modification.
// The .NET Framework 1.1 way to create a list: list1 = new (); (3); (105); list2 = new (); ("It is raining in Redmond."); ("It is snowing in the mountains.");
But this convenience comes at a price. Any reference or value type added to the ArrayList will be implicitly cast to an Object. If the item is a value type, it must be boxed when it is added to the list and unboxed when searching. Cases, as well as packing and unboxing operations, all degrade performance; the effects of packing and unboxing are obvious when a large collection has to be circulated.
Another limitation is the lack of compile-time type checking; because ArrayList will cast all items to Object, it is impossible to prevent client code from performing operations like the following during compilation:
list = new (); // Add an integer to the list. (3); // Add a string to the list. This will compile, but may cause an error later. ("It is raining in Redmond."); int t = 0; // This causes an InvalidCastException to be returned. foreach (int x in list) { t += x; }
Although the practice of combining strings and ints in a single ArrayList is completely acceptable when creating heterogeneous collections and sometimes requires intentionality, this practice is likely to produce a programming error and will not be detected until runtime.
In versions 1.0 and 1.1 of the C# language, you can only avoid the danger of common code in the .NET Framework base class library collection class by writing your own type-specific collections. Of course, since such a class cannot be reused for multiple data types, the advantage of generalization is lost, and you have to rewrite the class for each type you want to store.
What ArrayList and other similar classes really need is how client code specifies the specific data type to use for each instance. This will no longer require upward casting to T:, and also enable the compiler to perform type checking. In other words, ArrayList requires a type parameter. This is exactly what generics can provide. In a generic List<T> collection of N: namespace, adding items to the collection is similar to the following:
// The .NET Framework 2.0 way to create a list List<int> list1 = new List<int>(); // No boxing, no casting: (3); // Compile-time error: // ("It is raining in Redmond.");
For client code, the only syntax added when using List<T> is the type parameter in declaration and instantiation compared to ArrayList. While this approach slightly increases the complexity of encoding, the benefit is that you can create a safer and faster list than ArrayList, especially if the list item is a value type.