C# Tutorial — Generics
Unleash the power of <T> for ultimate code reusability and for convenience when working with multiple data types.
The use of Generics facilitates the creation of classes and methods that can work with any data type, be it custom or framework specific. This comes in handy when you want to create code that can be reused with multiple types of data.
This is a relatively easier concept to grasp and has the power to elevate your code reusability to the sky.
Scenario
Display all the values in the collections to the user.
The Wrong Way — What No To Do
namespace Theta.GenericNonGenericDemo;
public class NonGenericDemo {
public List<string> employeeNames = new();
public List<int> employeeIDs = new();
public List<double> employeeSalaries = new();
public NonGenericDemo() {
employeeNames.Add("Namal");
employeeIDs.Add(1021);
employeeSalaries.Add(10000.05);
/* Feel free to populate more employeess :) */
}
public void DisplayEmployeeData(List<string> employees) {
foreach (string data in employees) {
/* Some complex business logic goes here */
Console.Write($"{data} ");
}
/* Just prints a new line, to make it look nice :) */
Console.WriteLine(Environment.NewLine);
}
public void DisplayEmployeeData(List<int> employees) {
foreach (int data in employees) {
Console.Write($"{data} ");
}
Console.WriteLine(Environment.NewLine);
}
public void DisplayEmployeeData(List<double> employees) {
foreach (double data in employees) {
Console.Write($"{data} ");
}
Console.WriteLine(Environment.NewLine);
}
}
In this way, there are multiple DisplayEmployeeData methods to display the data of each collection. This method is only responsible for displaying the data and nothing else therefore;
Now think about a scenario where there are many more data types and you want to modify the DisplayEmployeeData to display data in a different style or manner, you will have to modify each method consuming valuable time and resources.
The Right Way — Reusability
namespace Theta.GenericNonGenericDemo;
public class GenericDemo {
public List<string> employeeNames = new();
public List<int> employeeIDs = new();
public List<double> employeeSalaries = new();
public GenericDemo() {
employeeNames.Add("Namal");
employeeIDs.Add(1021);
employeeSalaries.Add(10000.05);
/* Feel free to populate more employeess :) */
}
/* T - can be any name */
public void DisplayEmployeeData<T>(List<T> employees) where T : notnull {
foreach (T data in employees) {
/* Some complex business logic goes here */
Console.Write($"{data} ");
}
/* Just prints a new line, to make it look nice :) */
Console.WriteLine(Environment.NewLine);
}
}
In this way, there’s only method DisplayEmployeeData to handle and display data. This method can handle any data type. The T inside of <T> can be any allowed name.
public void DisplayEmployeeData<T>(List<T> employees) where T : notnull
The keyword where is optional here but with this keyword, we can specify certain restrictions for the what the method receives as input.
Remember
Things to remember about using Generic types.
- You can maximise code reuse, performance and readability.
- You can create your own generic methods, interfaces, classes or delegates.
- Use proper validations to ensure the full power of generic data types.