Introduction to Generics in C#

Generics allow you to delay the specification of the data type of programming elements in a class or a method, until it is actually used in the program. In other words, generics allow you to write a class or method that can work with any data type.

You write the specifications for the class or the method, with substitute parameters for data types. When the compiler encounters a constructor for the class or a function call for the method, it generates code to handle the specific data type.


 
 
Generic classes and methods combine reusability, type safety and efficiency in a way that their non-generic counterparts cannot. Generics are most frequently used with collections and the methods that operate on them. Version 2.0 of the .NET Framework class library provides a new namespace, System.Collections.Generic, that contains several new generic-based collection classes. It is recommended that all applications that target the .NET Framework 2.0 and later use the new generic collection classes instead of the older non-generic counterparts such as ArrayList.
 
Features of Generics
 
Generics is a technique that enriches your programs in the following ways:
  • It helps you to maximize code reuse, type safety and performance.
  • You can create generic collection classes. The .NET Framework class library contains several new generic collection classes in the System.Collections.Generic namespace. You may use these generic collection classes instead of the collection classes in the System.Collections namespace.
  • You can create your own generic interfaces, classes, methods, events and delegates.
  • You may create generic classes constrained to enable access to methods on specific data types.
  • You may get information on the types used in a generic data type at run-time using reflection.


 

  1. // Declare the generic class.   
  2. public class GenericList<T>  
  3. {  
  4.     void Add(T input) { }  
  5. }  
  6. class TestGenericList  
  7. {  
  8.     private class ExampleClass { }  
  9.     static void Main()  
  10.     {  
  11.         // Declare a list of type int.  
  12.         GenericList<int> list1 = new GenericList<int>();  
  13.   
  14.         // Declare a list of type string.  
  15.         GenericList<string> list2 = new GenericList<string>();  
  16.   
  17.         // Declare a list of type ExampleClass.  
  18.         GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();  
  19.     }  

Generic Methods


In the previous example, we used a generic class; we can declare a generic method with a type parameter. The following program illustrates the concept:
  1. using System;    
  2. using System.Collections.Generic;    
  3.    
  4. public class Program    
  5.   {    
  6.       static void Swap<T>(ref T lhs, ref T rhs)    
  7.       {    
  8.          T temp;    
  9.          temp = lhs;    
  10.          lhs = rhs;    
  11.          rhs = temp;    
  12.       }    
  13.       static void Main(string[] args)    
  14.       {    
  15.          int a, b;    
  16.          char c, d;    
  17.          a = 10;    
  18.          b = 20;    
  19.          c = 'I';    
  20.          d = 'V';    
  21.              
  22.          // Display values before swap:    
  23.          Console.WriteLine("Int values before calling swap:");    
  24.          Console.WriteLine("a = {0}, b = {1}", a, b);    
  25.          Console.WriteLine("Char values before calling swap:");    
  26.          Console.WriteLine("c = {0}, d = {1}", c, d);    
  27.              
  28.          // Call Generic Swap    
  29.          Swap<int>(ref a, ref b);    
  30.          Swap<char>(ref c, ref d);    
  31.              
  32.          // Display values after swap:    
  33.          Console.WriteLine("Int values after calling swap:");    
  34.          Console.WriteLine("a = {0}, b = {1}", a, b);    
  35.          Console.WriteLine("Char values after calling swap:");    
  36.          Console.WriteLine("c = {0}, d = {1}", c, d);    
  37.              
  38.          Console.ReadKey();    
  39.       }    
  40. }    

When the preceding code is compiled and executed, it produces the following result:

Int values before calling swap: a = 10, b = 20 
Char values before calling swap: c = I, d = V
Int values after calling swap: a = 20, b = 10
Char values after calling swap: c = V, d = I
Generic Delegates
 
You can define a generic delegate with type parameters. For example:
  1. delegate T NumberChanger<T>(T n); 
The following example shows use of this delegate:
  1. using System;    
  2. using System.Collections.Generic;    
  3.     
  4. delegate T NumberChanger<T>(T n);     
  5. public  class TestDelegate    
  6. {    
  7.       static int num = 10;    
  8.       public static int AddNum(int p)    
  9.       {    
  10.          num += p;    
  11.          return num;    
  12.       }    
  13.           
  14.       public static int MultNum(int q)    
  15.       {    
  16.          num *= q;    
  17.          return num;    
  18.       }    
  19.       public static int getNum()    
  20.       {    
  21.          return num;    
  22.       }    
  23.           
  24.       static void Main(string[] args)    
  25.       {    
  26.          // Create delegate instances    
  27.          NumberChanger<int> nc1 = new NumberChanger<int>(AddNum);    
  28.          NumberChanger<int> nc2 = new NumberChanger<int>(MultNum);    
  29.              
  30.          // Calling the methods using the delegate objects    
  31.          nc1(25);    
  32.          Console.WriteLine("Value of Num: {0}", getNum());    
  33.          nc2(5);    
  34.          Console.WriteLine("Value of Num: {0}", getNum());    
  35.          Console.ReadKey();    
  36.       }    
  37. }   
When the preceding code is compiled and executed, it produces the following result:

Value of Num: 35
Value of Num: 175
 
Generic Interfaces
 
It is often useful to define interfaces either for generic collection classes, or for the generic classes that represent items in the collection. The preference for generic classes is to use generic interfaces, such as IComparable(Of T) rather than IComparable, to avoid boxing and unboxing operations on value types. The .NET Framework class library defines several generic interfaces for use with the collection classes in the System.Collections.Generic namespace.
 
Generic interface example:
  1. interface IBaseInterface<T> { }  
  2.   
  3. class SampleClass : IBaseInterface<string> { }