Author : Rahul
Last Modified : 04-Aug-2021
Complexity : Beginner

Generics in C#


Introduction

The Generics in C# is introduced in C# 2.0. Generics allow you to write a class or method that can work with any data type. Before the introduction of Generics, if there is a method that performs the same operation with the different data types, function overloading is a good idea but after the introduction of Generics, we create a common method and pass data type with method call so that one method will work with different data types.

We write the class or method with a parameter that can replace with data type at compile time. When we write the class constructor or method with a particular data type, this data type is replaced in the whole class or method with this data type. So a single class or method will work with different data types.

The real power of Generics is code reusability and type safety.  


Why Generics Required

Let us try to understand the need for Generics in C# with one example.

Let us create a simple program to add two integer numbers.

using System;

namespace DemoApplication
{
 public class Program
 {
   static void Main(string[] args)
   {
     int result = clsCalculator.AddValues(10, 20);
     Console.WriteLine($"Sum:{result}");
     Console.Read();
   }
 }
 
 public class clsCalculator
 {
   public static int AddValues(int firstNum, int secondNum)
   {
     return firstNum + secondNum;
   }
 }
}

Output

Sum:30

Explanation

The above program is very simple. clsCalculator class contains one method AddValues which is used to add two integer numbers and program class is used to call AddValues method with two numbers and display the result.

After some time we got new requirements and we want our clsCalculator class to also perform addition with float values. Since AddValues is tightly coupled with the int variable, we can not pass float values into this method. The solution for the above problem is to create another method that accepts float values to perform the addition.

So our clsCalculator class will look like this:

 public class clsCalculator
 {
   public static int AddValues(int firstNum, int secondNum)
   {
     return firstNum + secondNum;
   }
   
   public static float AddValues(float firstNum, float secondNum)
   {
     return firstNum + secondNum;
   }
 }

To resolve the problem we used function overloading but there is one problem which is code duplicacy. Here the logic is the same for both methods except for data types. Suppose in future we want to add two strings, we will create another overloaded method for adding two strings.

Another possible solution is to create one method which accepts object type data and perform the operation because all data types inherit from object types.

So now our class will look like this:

 public class clsCalculator
 {
   public static object AddValues(object firstNum, object secondNum)
   {
     return (int)firstNum + (int)secondNum;
   }
 }

At first look, this class looks ok but there is also one problem. Since we have store values in the object type parameter so that after passing values from the client-side, boxing will be applied to the values. within the method before adding, unboxing will be applied on the values and before sending the result again boxing applied into result and on the client-side again unboxing will be applied to show the result. Performing Boxing and Unboxing, again and again, will downgrade the performance. So this is not a good idea.

Now Generics will be a good idea to resolve this problem.

Syntex

public static T AddValues<T>(T firstNum, T secondNum)

Explanation

T: T is the replacer which contains the type and its values will be given when we call this method.
AddValues<int>(10,20): Here T will be replaced with int and method will perform operation on int data types.
AddValues<float>(10,20): Here T will be replaced with float and method will perform operation on float data types.
AddValues<string>("10","20"): Here T will be replaced with string and method will perform operation on string data types.

 

Example

Generic Class

using System;

namespace DemoApplication
{
 public class Program
 {
   static void Main(string[] args)
   {
     //Create class for int number
     clsNumber<int> intNumber = new clsNumber<int>();
     intNumber.data = 10;
     Console.WriteLine($"Data:{intNumber.GetData()}");
     
     //Create class for string
     clsNumber<string> stringNumber = new clsNumber<string>();
     stringNumber.data = "Rahul Goel";
     Console.WriteLine($"Data:{stringNumber.GetData()}");
     
     Console.Read();
   }
 }
 
 /// <summary>
 /// Generic Class
 /// </summary>
 /// <typeparam name="T"></typeparam>
 public class clsNumber<T>
 {
   public T data;
   public T GetData()
   {
     return data;
   }
 }
}

Output

Data:10
Data:Rahul Goel

Explanation

Here we create a Generic class. Within main method, 
On first section, we create class object for int values so T is replaced with int and perform the operation on int data type. On next section we create class object for string values so T is replaced with string data type and perform the operation on string values.

 

Generic Method

using System;

namespace DemoApplication
{
 public class Program
 {
   static void Main(string[] args)
   {
     int resultint = clsCalculator.AddValues<int>(10, 20);     
     float resultfloat = clsCalculator.AddValues<float>(10.5, 20.5);
     
     Console.WriteLine($"SumInt:{resultint} and SumFloat:{resultfloat}");
     Console.Read();
   }
 }
 
 public class clsCalculator
 {
   public static T AddValues<T>(T firstNum, T secondNum)
   {
     dynamic x = firstNum;
     dynamic y = secondNum;
     return x + y;
   }
 }
}

Output

SumInt:30 and SumFloat:31.0

Explanation

Here on first line, we pass int into AddValues method so T is replaced with int and perform the operation on int values. On next line we pass float into AddValues method so T is replaced with float and perform the operation on float values.

 

Uses of Generics

Generics can be applied to the following:

  1. Interface
  2. Abstract class
  3. Class
  4. Method
  5. Static method
  6. Property
  7. Event
  8. Delegates
  9. Operator

 

Advantage of Generics

  1. Code Reusability: Code reusability is increased after using Generics. Suppose we create one Generic method for adding two numbers. This method will work with int numbers as well as float numbers.
  2. Performance: Since Generics does not require boxing and unboxing so they improve performance.
  3. Type Safety: If we specify one data type in definition and try to use another data type, the compiler gives an error at compile time. In other words, Generics provide type safety.