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
{
//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
{
}
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
ReverseAndPrint
ReverseAndPrint
Console.Read();
}
//Generic method which takes an array type data
public static void ReverseAndPrint
{
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:
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
Swap
Swap
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
{
MyType t;
t = x;
x = y;
y = t;
}
}
}
Output:
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
Print
Print
Console.Read();
}
static void Print
{
Console.WriteLine("You have given " + x + " and " + y, "Result");
}
}
}
Output:
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
Repeate
Repeate
Console.Read();
}
static void Repeate
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++)
{
sb.Append(t).Append(" ");
}
Console.WriteLine(sb.ToString());
}
}
}
Output:
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.SetData(10);
Console.WriteLine("Given Value is : " + objMyGenericClassInt.GetData());
//Passing 'string' data-type as input
MyGenericClass
objMyGenericClassString.SetData("Welcome");
Console.WriteLine("Given string is : " + objMyGenericClassString.GetData());
//Passing 'DateTime' data-type as input
MyGenericClass
objMyGenericClassDateTime.SetData(DateTime.Today);
Console.WriteLine("Today's Date is : " + objMyGenericClassDateTime.GetData());
Console.Read();
}
}
}
Output: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
{
MyType T;
public void SetData(MyType T)
{
this.T = T;
}
public MyType GetData()
{
return T;
}
}
class GenericClassSample
{
static void Main(string[] args)
{
MyGenericClass
objMyGenericClassInt.SetData(10);
Console.WriteLine("Given Value is : " + objMyGenericClassInt.GetData());
MyGenericClass
objMyGenericClassDateTime.SetData(DateTime.Today);
Console.WriteLine("Today's Date is : " + objMyGenericClassDateTime.GetData());
MyGenericClass
objMyGenericClassDbl.SetData(10.65);
Console.WriteLine("Given Value is : " + objMyGenericClassDbl.GetData());
Console.Read();
}
}
}
Output:
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
{
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.SetData("Mahesh");
Console.WriteLine("Given String Value is : " + objMyGenericClassString.GetData());
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 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:
I hope you got good idea about Generics
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.."