Ads block

Banner 728x90px

c#step31


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:
Example: 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.
C# Generic Class
The above MyGenericClass<int> class would be compiled, as shown below.
Example: Compiled 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 Class as 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
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.
Example: 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 Generics

  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.
 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.

C#: Constraints in Generics

You have learned abut the generics in the previous section. Generics allow you to define a class with placeholders for the type of its fields, methods, parameters, etc. Consider the following example of a generic class.
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; }
}
In the above example, the generic class MyGenericClass defines a placeholder for the type, but the placeholder is like a black box, because MyGenericClass doesn't know anything about the placeholder type, whether it is primitive or non-primitive type, or an interface or custom class etc.
C# includes Constraints to specify which type of placeholder type with the generic class is allowed. It will give a compile time error if you try to instantiate a generic class using a placeholder type that is not allowed by a constraints. For example, if the generic constraints specifies that only reference type can be used with the generic class then you cannot use value type to create an object of generic type.
Constraints can be applied using the where keyword. In the following example, MyGenericClass specifies the constraints that only a reference type can be used with MyGenericClass. This means that only a class can be a placeholder type not the primitive types, struct etc.
Example: Generic Class with Constraints
class MyGenericClass<T> where T: class
{
    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; }
}
So now, you cannot use int as a placeholder type. The following would give a compile time error.
Example: Compile Time Error
MyGenericClass<int> intGenericClass = new MyGenericClass<int>(10);
String or any class type is a valid type because it is a reference type.
Example: Intantiate Constrained Generic Class:
MyGenericClass<string> strGenericClass = new MyGenericClass<string>("Hello World");

MyGenericClass<Student> strGenericClass = new MyGenericClass<Student>(new Student());
The following table lists the types of generic constraints.
ConstraintDescription
where T : classType must be reference type.
where T: structType must be value type.
where T: new()Type must have public parameterless constructor.
where T: <base class name>Type must be or derive from the specified base class
where T: <interface name>Type must be or implement the specified interface.
where T: UType supplied for T must be or derive from the argument supplied for U.

Multiple constraints:

A generic class can have multiple constraints as shown below.
Multiple constraints:
class MyGenericClass<T, U> where T: class where U:struct
{
    ...
}

Constraint on Generic Methods

You can apply constraints on the generic methods also.
Method constraint:
class MyGenericClass<T> where T: class 
{
    public T genericMethod<U>(T genericParameter, U anotherGenericType) where U: struct
    {
        Console.WriteLine("Generic Parameter of type {0}, value {1}", typeof(T).ToString(),genericParameter);
        Console.WriteLine("Return value of type {0}, value {1}", typeof(T).ToString(), genericMemberVariable);
            
        return genericMemberVariable;
    }        
}
Thus, constraints can be applied on generic types.
 Points to Remember :
  1. Constraints specifies the kind of types allowed with the generics.
  2. Constraints can be applied using the where keyword.
  3. Six types of constraints can be applied: class, struct, new(), base class name, interface and derived type.
  4. Multiple constraints also can be applied.

No comments:

Post a Comment