Generics in C#

Generics introduced in C# 2.0. Generics allow you to define a class with placeholders for the type of its fields, methods, parameters, etc. Generics replace these placeholders with some specific type at compile time.

A generic class can be defined using angle brackets <>. For example, the following is a simple generic class with a generic member variable, generic method and property.

Example: Generic class

class MyGenericClass<T>
{
    private T genericMemberVariable;

    public MyGenericClass(T value)
    {
        genericMemberVariable = value;
    }

    public T genericMethod(T genericParameter)
    {
        Console.WriteLine("Parameter type: {0}, value: {1}", typeof(T).ToString(),genericParameter);
        Console.WriteLine("Return type: {0}, value: {1}", typeof(T).ToString(), genericMemberVariable);
            
        return genericMemberVariable;
    }

    public T genericProperty { get; set; }
}

As you can see in the above code, MyGenericClass is defined with <T>. <> indicates that MyGenericClass is generic and the underlying type would be defined later, for now consider it as T . You can take any character or word instead of T.

Now, the compiler assigns the type based on the type passed by the caller when instantiating a class. For example, the following code uses the int data type:

Instantiate generic class:

MyGenericClass<int> intGenericClass = new MyGenericClass<int>(10);

int val = intGenericClass.genericMethod(200);

Output:
Parameter type: int, value: 200
Return type: int, value: 10

The following figure illustrates how the compiler will replace T with int in MyGenericClass.

Generic class

The above MyGenericClass<int> class would be compiled, as shown below.

Example: Generic class

class MyGenericClass
{
    private int genericMemberVariable;

    public MyGenericClass(int value)
    {
        genericMemberVariable = value;
    }

    public int genericMethod(int genericParameter)
    {
        Console.WriteLine("Parameter type: {0}, value: {1}", typeof(int).ToString(), genericParameter);
        Console.WriteLine("Return type: {0}, value: {1}", typeof(int).ToString(), genericMemberVariable);

        return genericMemberVariable;
    }

    public int genericProperty { get; set; }

}

You can use any type while instantiating a MyGenricClass. For example, the following example uses a string type.

Example: Generic class

MyGenericClass<string> strGenericClass = new MyGenericClass<string>("Hello Generic World");

strGenericClass.genericProperty = "This is a generic property example.";
string result = strGenericClass.genericMethod("Generic Parameter");

Output:
Parameter type: string, value: Generic Parameter
Return type: string, value: Hello Generic World

Generic base class:

When deriving from a generic base class, you must provide a type argument instead of the base-class's generic type parameter as shown below.

Example: Generic base class

class MyDerivedClass : MyGenericClass<string>
{ 
    //implementation
}
    

If you want the derived class to be generic then no need to specify type for the generic base class.

Example: Generic derived class

class MyDerivedClass<U> : MyGenericClass<U>
{ 
    //implementation
}
    

If the generic base class has constraints, the derived class must use the same constraints.


class MyGenericClass<T> where T: class 
{
        // Implementation 
}

class MyDerivedClass<U> : MyGenericClass<U> where U: class
{ 
        //implementation
}

Generic Delegates:

As you have already learned in the previous section, the delegate defines the signature of the method which it can invoke. A generic delegate can be defined the same way as delegate but with generic type.

For example, consider the following generic delegate that takes two generic parameters.

Example: Generic Delegate

class Program
{
    public delegate T add<T>(T param1, T param2);

    static void Main(string[] args)
    {
        add<int> sum = AddNumber;

        Console.WriteLine(sum(10, 20));

        add<string> conct = Concate;

        Console.WriteLine(conct("Hello","World!!"));
    }

    public static int AddNumber(int val1, int val2)
    {
        return val1 + val2;
    }

    public static string Concate(string str1, string str2)
    {
        return str1 + str2;
    }
}

Output:
30
Hello World!!

In the above example, add delegate is generic. In the Main() method, it has defined add delegate of int type variable sum. So it can point to the AddNumber() method which has int type parameters. Another variable of add delegate uses string type, so it can point to the Concate method. In this way, you can use generic delegates for different methods of different types of parameters.

Note : A generic delegate can point to methods with different parameter types. However, the number of parameters should be the same.

Generics can be applied to the following:

  • Interface
  • Abstract class
  • Class
  • Method
  • Static method
  • Property
  • Event
  • Delegates
  • Operator

Advantages of Generic:

  1. Increases the reusability of the code.
  2. Generic are type safe. You get compile time errors if you try to use a different type of data than the one specified in the definition.
  3. Generic has a performance advantage because it removes the possibilities of boxing and unboxing.

Further reading:

Points to Remember :

  1. Generics denotes with angel bracket <>.
  2. Compiler applys specified type for generics at compile time.
  3. Generics can be applied to interface, abstrct class, method, static method, property, event, delegate and operator.
  4. Generics performs faster by not doing boxing & unboxing.