About C#.Net Generics concepts


This article will provide you very basic understanding about "Generics" concept. After reading this article you will get good idea about Generics, how to use different types of Generics like Generic methods, Generic Classes and Generic interfaces. I have provided very examples and also included screen shots of the program output.

Introduction



We know that .Net provides many pre-defined generic classes, methods and interfaces but still some times we have create our own generic classes or methods or interfaces. So here I have concentrated only on creating our own customized generic types with very simple examples.

Generics

Generics allow us to define type-safe data structures, without specifying the actual type of data (i.e. Data-Type). The advantage of using generics is re-usability of code without duplicating type-specific code. Conceptually generics are similar to C++ templates, but are drastically different in implementation and capabilities.

Types of Generics:

1. Generic Methods
2. Generic Classes
3. Generic Interfaces

Definition of Generic Methods:

A method that contains an argument of the "generic" type means Generic methods are the methods that can operate on any type of data.

For example, logic for sorting / swapping will be same whatever the type of array is. Hence, you create a single method that can operate on any type of data instead of creating one method for each type.

To create a method as generic method declare the generic types required for the method immediately after the method name in angular brackets.

Access-modifier ReturnType MethodName(arguments)
{
//Logic Need to add
}

Advantage of Generic Methods:

According to generic methods, when you call a method, you can pass difference types of arguments to the same method, without overloading it.

Implementation of Generic Methods

Method Definition with Generics:

Access-modifier returntype MethodName(T arg)
{

}

Method Calling: MethodName(value);

While calling the generic method you must pass the data types on which the method has to operate as arguments for generic types. Immediately after the method name in angular brackets

Example: Using generics concept, write the logic to print the values of integer array, string array and a double array in reverse order.

namespace GenericsExamples
{
class GenericMethodsSample
{
static void Main(string[] args)
{
int[] intArray = { 10, 20, 30, 40, 50 };
string[] stringArray = { "Kumar", "Mahesh", "Durgam" };
double[] doubleArray = { 3.567, 7.891, 2.345 };
ReverseAndPrint(intArray);
ReverseAndPrint(stringArray);
ReverseAndPrint(doubleArray);
Console.Read();
}

//Generic method which takes an array type data
public static void ReverseAndPrint(T[] arr)
{
Array.Reverse(arr); //Here Array is a predefined Class
foreach (T item in arr)
Console.Write(item + " ");
Console.WriteLine("");
}
}
}


Note: Here array class is a predefined class which has some pre-defined methods like Reverse or Sort etc. To reverse/sort the given array elements

Output:
Generic Method Example1

Example: Using generics concept, swapping the given two integers, strings and doubles.

namespace GenericsExamples
{
class GenericMethodsSample
{
static void Main()
{
int i = 10, j = 20;
string string1 = "Mahesh", string2 = "Durgam";
double d1 = 45.56, d2 = 75.89;
Console.WriteLine("Before Swapping");
Console.WriteLine("Value of i : " + i + " Value of j : " + j);
Console.WriteLine("Value of string1 : " + string1 + " Value of string2 : " + string2);
Console.WriteLine("Value of d1 : " + d1 + " Value of d2 : " + d2);
Swap(ref i, ref j);
Swap(ref string1, ref string2);
Swap(ref d1, ref d2);
Console.WriteLine("\n");
Console.WriteLine("After Swapping");
Console.WriteLine("Value of i : " + i + " Value of j : " + j);
Console.WriteLine("Value of string1 : " + string1 + " Value of string2 : " + string2);
Console.WriteLine("Value of d1 : " + d1 + " Value of d2 : " + d2);
Console.Read();
}

public static void Swap(ref MyType x, ref MyType y)
{
MyType t;
t = x;
x = y;
y = t;
}
}
}


Output:

Generic Method Example2

Till now we have seen generic methods taking only single Generic type, now we will see how we can pass multiple generic types to a method as parameters

Generic Methods with Multiple Generic Types:

You may want to create a generic method that takes arguments of different types and not all arguments of same type. In this case you have to create the generic method with multiple generic types by separating them with comma in angular brackets immediately after the method name.

When a generic method is created with multiple generic types then while calling that method you must pass multiple data types as arguments on for each generic type. See the below simple example of creating multiple generic types.

Example: Simple example to use multiple generic types as input and display them.

namespace GenericsExamples
{
class GenericMethodsSample
{
static void Main()
{
Print(10, 45.56);
Print("MaheshDurgam", DateTime.Now);
Print("Mahesh", "Vijay");
Console.Read();
}

static void Print(MyType1 x, MyType2 y)
{
Console.WriteLine("You have given " + x + " and " + y, "Result");
}
}
}


Output:
Generic Method Example3

Generic Methods with Standard Types:

It is not compulsory that every parameter and variable in a generic method must be of generic type and a generic method can have the parameter and variables of standard types like int, double, string etc. But in this case the argument sent to that parameter must be of that particular type.

Example: Simple example using one generic type and standard type as input. This example will repeat given input with specified number of times.

using System.Text;

namespace GenericsExamples
{
class GenericClassSample
{
static void Main()
{
Repeate("Mahesh", 10);
Repeate(5, 3);
Repeate(3.2, 5);
Console.Read();
}

static void Repeate(MyType t, int n)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++)
{
sb.Append(t).Append(" ");
}
Console.WriteLine(sb.ToString());
}
}
}


Output:
Generic Method with StandardTypes

Generic Classes

A generic class is a class whose objects can work on different type of data. To create a generic class and declare the generic types required for the class immediately after the class name in angular brackets.

This should specify the generic type name after the class name as follows:

Syntax:

[access Modifier] class ClassName
{
//Your Logic
}

When a class is a generic class then while creating an object for the class you must pass the data types on which the generic types has to operate as argument to the generic type.

Advantage of Generic Classes:

According to generic classes, when an instance is created, the required type of values could be stored as the data members.
Implementation of Generic Classes

Class Definition with Generics:

class classname
{
Access-modifier DatamemberName;
}

Object Construction:

classname objname = new classname;

Example: Simple example of creating a Generic Class, which takes any type of data as input and returns that data.

using System.Text;

namespace GenericsExamples
{
//Generic Class which will take input of any type
public class MyGenericClass
{
MyType T;

public void SetData(MyType T)
{
this.T = T;
}

public MyType GetData()
{
return T;
}
}

class GenericClassSample
{
static void Main()
{
//Passing 'int' data-type as input
MyGenericClass objMyGenericClassInt = new MyGenericClass();
objMyGenericClassInt.SetData(10);
Console.WriteLine("Given Value is : " + objMyGenericClassInt.GetData());

//Passing 'string' data-type as input
MyGenericClass objMyGenericClassString = new MyGenericClass();
objMyGenericClassString.SetData("Welcome");
Console.WriteLine("Given string is : " + objMyGenericClassString.GetData());

//Passing 'DateTime' data-type as input
MyGenericClass objMyGenericClassDateTime = new MyGenericClass();
objMyGenericClassDateTime.SetData(DateTime.Today);
Console.WriteLine("Today's Date is : " + objMyGenericClassDateTime.GetData());

Console.Read();
}
}
}


Output:
Generic Class Example 1

Constraints on Generic Types



By default a generic type can accept almost any type. But there may be a situation where you have to restrict your generic types to accept only certain types and not every type. And for this you have to specify constraints on generic types.

To specify the constraints on generic types you need to use the keyword "where" at the end of method declaration.

Syntax: Where generic type : constraint

You can specify the following constraints on generic types.

Struct: When you want to restrict your generic type from accepting only value types then specify the constraint as "struct"

Class: When you want to restrict your generic type from accepting only reference types then specify the constraint as "class"

Class Name: When you specify a "Class Name" as constraint then the generic type can accept only that particular class and its derived classes, but not any other type.

Interface Name: When you specify "Interface Name" as constraint then the generic type will accept only those types that implement the interface.

Note: You can also specify multiple constraints on generic types and for this you have to enclose the constraints within { }, by separating them with "," (Comma).

For example: if you specify the constraint as {struct, interface name} then the generic type will accept only the value types that also that implement the specified interface.

But you cannot specify the combination of constraints like struct & class or class & struct, because struct is value type and class is reference type.

Example: creating a generic Class using Constraint 'Struct', means the generic class will accept only value types

namespace GenericsExamples
{
public class MyGenericClass where MyType : struct
{
MyType T;

public void SetData(MyType T)
{
this.T = T;
}

public MyType GetData()
{
return T;
}
}

class GenericClassSample
{
static void Main(string[] args)
{
MyGenericClass objMyGenericClassInt = new MyGenericClass();
objMyGenericClassInt.SetData(10);
Console.WriteLine("Given Value is : " + objMyGenericClassInt.GetData());

MyGenericClass objMyGenericClassDateTime = new MyGenericClass();
objMyGenericClassDateTime.SetData(DateTime.Today);
Console.WriteLine("Today's Date is : " + objMyGenericClassDateTime.GetData());

MyGenericClass objMyGenericClassDbl = new MyGenericClass();
objMyGenericClassDbl.SetData(10.65);
Console.WriteLine("Given Value is : " + objMyGenericClassDbl.GetData());

Console.Read();
}
}
}


Output:

Generic Class Example 2

Example: creating a generic Class using Constraint 'Class', means the generic class will accept only reference types (only class type variables)

using System.Collections.Generic;

namespace GenericsExamples
{
//Creating our own custom Generic class
public class MyGenericClass where MyType : class
{
MyType T;

public void SetData(MyType T)
{
this.T = T;
}

public MyType GetData()
{
return T;
}
}

//Creating Employee class
class Employee
{

public int EmpId { get; set; }
public string EmpName { get; set; }
public double Salary { get; set; }

}

//Using the Generic class
class GenericClassSample
{
static void Main(string[] args)
{
MyGenericClass objMyGenericClassString = new MyGenericClass();
objMyGenericClassString.SetData("Mahesh");
Console.WriteLine("Given String Value is : " + objMyGenericClassString.GetData());

MyGenericClass objMyGenericClassEmp = new MyGenericClass();
Employee objEmp = new Employee();
objEmp.EmpId = 100;
objEmp.EmpName = "Mahesh Kumar";
objEmp.Salary = 25000;
objMyGenericClassEmp.SetData(objEmp);
Console.WriteLine("\nCustom Employee Class");
Console.WriteLine("Employee Id is : " + objMyGenericClassEmp.GetData().EmpId);
Console.WriteLine("Employee Name is : " + objMyGenericClassEmp.GetData().EmpName);
Console.WriteLine("Employee Salary is : " + objMyGenericClassEmp.GetData().Salary);

Console.Read();
}
}
}


Output:

Generic Class with Constraint

Generic Interfaces

Just like generic classes, you can also create generic interfaces which can be used in different classes that need to follow the same structure in all the classes. Additionally you can pass the generic type to the interface, so that it will work on only those types.

This should specify the generic type name after the class name as follows:

interface InterfaceName
{
//Interface Method declarations
}

Implementation of Generic Interfaces

Class Definition with Generics Interface:

class classname : InterfaceName
{
//Implement the interface methods
}

Example: creating and using a simple generic Interface

using System.Collections.Generic;

namespace GenericsExamples
{
//Creating own custom Generic Interface
public interface IOperations
{
T Add(T arg1, T arg2);
T Subtract(T arg1, T arg2);
T Multiply(T arg1, T arg2);
T Divide(T arg1, T arg2);
}

//Implementing Generic Interface for Integer operations in a class
public class MathClassInt : IOperations
{
public int Add(int arg1, int arg2)
{
return arg1 + arg2;
}

public int Subtract(int arg1, int arg2)
{
return arg1 - arg2;
}

public int Multiply(int arg1, int arg2)
{
return arg1 * arg2;
}

public int Divide(int arg1, int arg2)
{
return arg1 / arg2;
}
}

//Implementing Generic Interface for Double operations in a class
public class MathClassDouble : IOperations
{
public double Add(double arg1, double arg2)
{
return arg1 + arg2;
}

public double Subtract(double arg1, double arg2)
{
return arg1 - arg2;
}

public double Multiply(double arg1, double arg2)
{
return arg1 * arg2;
}

public double Divide(double arg1, double arg2)
{
return arg1 / arg2;
}
}

//Implementing Both the above classes
public class TestProgram
{
static void Main(string[] args)
{
Console.WriteLine("***** Generic Interfaces : Using Int *****\n");

MathClassInt objMathClassInt = new MathClassInt();
Console.WriteLine("Addition of Two Numbers :{0}", objMathClassInt.Add(10, 20));
Console.WriteLine("Subtraction of Two Numbers :{0}", objMathClassInt.Subtract(20, 10));
Console.WriteLine("Multiplication of Two Numbers :{0}", objMathClassInt.Multiply(10, 20));
Console.WriteLine("Division of Two Numbers :{0}", objMathClassInt.Divide(20, 10));

Console.WriteLine("\n***** Generic Interfaces : Using Double *****\n");

MathClassDouble objMathClassDouble = new MathClassDouble();
Console.WriteLine("Addition of Two Numbers :{0}", objMathClassDouble.Add(10.25, 20.50));
Console.WriteLine("Subtraction of Two Numbers :{0}", objMathClassDouble.Subtract(20.15, 10.18));
Console.WriteLine("Multiplication of Two Numbers :{0}", objMathClassDouble.Multiply(10.50, 20.50));
Console.WriteLine("Division of Two Numbers :{0}", objMathClassDouble.Divide(20.23, 10.12));
Console.ReadLine();
}
}
}

Output:

About C#.Net Generics concepts


I hope you got good idea about Generics


Attachments

Comments

Author: Sridhar Thota28 May 2015 Member Level: Gold   Points : 4

Hi Mahesh.

Good article on generics. Let add few lines.

Generics are same like c++ templates.
1.When functionality of more than one method is
same, instead of going for overloading methods
we can write a single generic method which will
work for all.
2.One generic data type can be used to work with
different data types.
3.We can have generic classes, generic methods
and generic data types.
4.Generics are type safe.

Regards

Sridhar.
DNS Member.
"Hope for the best.. Prepare for the worst.."



  • Do not include your name, "with regards" etc in the comment. Write detailed comment, relevant to the topic.
  • No HTML formatting and links to other web sites are allowed.
  • This is a strictly moderated site. Absolutely no spam allowed.
  • Name:
    Email: