Introduction

Generics in C# are a powerful feature that allows developers to write flexible, reusable, and type-safe code. Instead of creating separate classes or methods for different data types, generics enable you to define a single implementation that works with multiple types while maintaining compile-time type safety.

This article explains how to use generics in C#, why they are important, and how they improve code reusability and performance with real-world examples.

What Are Generics in C#?

Generics allow you to define classes, methods, interfaces, and delegates with a placeholder type (usually represented by T). This type is specified when the class or method is used.

Basic Example

class GenericClass<T>
{
    public T Data;

    public void ShowData(T value)
    {
        Console.WriteLine(value);
    }
}

class Program
{
    static void Main()
    {
        GenericClass<int> intObj = new GenericClass<int>();
        intObj.ShowData(10);

        GenericClass<string> strObj = new GenericClass<string>();
        strObj.ShowData("Hello Generics");
    }
}

In this example, the same class works with both int and string types without duplicating code.

Why Use Generics in C#?

Generics solve common problems related to code duplication and runtime errors.

Key Benefits

Generic Methods in C#

You can also create methods that work with different data types.

Example

class Utility
{
    public static void Print<T>(T value)
    {
        Console.WriteLine(value);
    }
}

class Program
{
    static void Main()
    {
        Utility.Print<int>(100);
        Utility.Print<string>("Reusable Method");
    }
}

This method can accept any type without needing multiple overloads.

Generic Collections in C#

C# provides built-in generic collections that are widely used in real-world applications.

Example Using List

using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<string> names = new List<string>();
        names.Add("Rahul");
        names.Add("Amit");

        foreach (var name in names)
        {
            Console.WriteLine(name);
        }
    }
}

Generic collections like List, Dictionary<TKey, TValue>, and Queue provide type safety and better performance.

Generic Constraints in C#

Sometimes you want to restrict the type that can be used with generics. This is done using constraints.

Example

class Calculator<T> where T : struct
{
    public T Value;
}

Here, T must be a value type (like int, double, etc.).

Common Constraints

Real-World Example of Generics

Consider a repository pattern in a real-world application.

class Repository<T>
{
    private List<T> items = new List<T>();

    public void Add(T item)
    {
        items.Add(item);
    }

    public List<T> GetAll()
    {
        return items;
    }
}

class Program
{
    static void Main()
    {
        Repository<string> repo = new Repository<string>();
        repo.Add("Data 1");
        repo.Add("Data 2");

        foreach (var item in repo.GetAll())
        {
            Console.WriteLine(item);
        }
    }
}

This allows the same repository to work with any entity type without rewriting code.

Difference Between Generics and Non-Generics

FeatureGenericsNon-Generics
Type SafetyCompile-timeRuntime
PerformanceBetter (no boxing)Slower (boxing/unboxing)
ReusabilityHighLow
Code ClarityClean and structuredLess clear

When to Use Generics

Use generics when:

Key Takeaways

Summary

Generics in C# provide a clean and efficient way to write reusable and type-safe code by allowing developers to define flexible components that work with multiple data types. They improve performance by avoiding unnecessary type conversions, enhance code readability, and reduce duplication. By using generics in classes, methods, and collections, developers can build scalable and maintainable applications that follow modern software development best practices.