Using Generics in C#: Benefits and Syntax

Write reusable, type-safe code with generic classes, methods, and collections.

Generics are a powerful feature in C# that allow you to write flexible, reusable code without sacrificing type safety. Instead of repeating code for every data type, you can define a class, method, or interface once β€” and use it with any type you need.

This lesson explains how generics work, why they’re useful, and how to use them effectively in your own projects.


πŸ” What Are Generics?

Generics let you write code that works with any data type, while still preserving compile-time type checking.

Instead of writing:

public class IntBox
{
    public int Value;
}

public class StringBox
{
    public string Value;
}

You write:

public class Box<T>
{
    public T Value;
}

Now Box<int> or Box<string> can be created from the same class, with strong typing.


🎯 Why Use Generics?

βœ… Type Safety
You avoid runtime casting and errors:

Box<int> numberBox = new Box<int>();
numberBox.Value = 42;

βœ… Code Reuse
Write once β€” use with any type.

βœ… Performance
Avoid boxing/unboxing (converting value types to object and back again).

βœ… Cleaner APIs
Collections like List<T> and Dictionary<TKey, TValue> rely on generics to be flexible but strongly typed.


πŸ“¦ Defining a Generic Class

public class Box<T>
{
    public T Content { get; set; }

    public void Print()
    {
        Console.WriteLine($"Box contains: {Content}");
    }
}

πŸ”Ή Example Usage:

Box<string> messageBox = new Box<string> { Content = "Hello!" };
messageBox.Print(); // Output: Box contains: Hello!

Box<int> intBox = new Box<int> { Content = 99 };
intBox.Print(); // Output: Box contains: 99

You can create as many versions as you like, and each one will behave exactly like a class written for that type.


βš™οΈ Generic Methods

You don’t always need to make a class generic β€” you can just create generic methods.

public class Helper
{
    public static void Swap<T>(ref T a, ref T b)
    {
        T temp = a;
        a = b;
        b = temp;
    }
}

πŸ”Ή Example:

int x = 5, y = 10;
Helper.Swap(ref x, ref y);
Console.WriteLine($"{x}, {y}"); // Output: 10, 5

This works with any type: int, string, even custom classes.


πŸ”‘ Constraints – Limiting Generic Types

You can restrict what types can be used with your generic classes or methods using constraints.

πŸ”Ή Where Clause Example:

public class Repository<T> where T : class
{
    public void Save(T item)
    {
        Console.WriteLine("Item saved.");
    }
}

Common Constraints:

ConstraintMeaning
where T : classOnly reference types allowed
where T : structOnly value types allowed
where T : new()Must have a public parameterless constructor
where T : BaseClassMust inherit from BaseClass
where T : InterfaceMust implement Interface

πŸ§ͺ Real-World Example: Generic Response Wrapper

public class ApiResponse<T>
{
    public bool Success { get; set; }
    public T Data { get; set; }
    public string Message { get; set; }
}

Usage:

ApiResponse<string> result = new ApiResponse<string>
{
    Success = true,
    Data = "User created",
    Message = "Operation successful"
};

Or:

ApiResponse<User> userResult = new ApiResponse<User>
{
    Success = true,
    Data = new User { Username = "james" },
    Message = "Found user"
};

You only define the structure once, and reuse it anywhere.


🚫 Without Generics: The Old Way

Before generics, developers used object, but this caused runtime errors and messy casting:

ArrayList list = new ArrayList();
list.Add("Hello");
string word = (string)list[0]; // Risky cast

βœ… With generics:

List<string> list = new List<string>();
list.Add("Hello");
string word = list[0]; // Safe, no cast needed

πŸ“š Summary Table

FeatureDescriptionExample
TGeneric type placeholderBox<T>
Generic ClassClass usable with any data typepublic class Box<T>
Generic MethodMethod usable with any typepublic void Swap<T>()
ConstraintLimit allowed types for genericswhere T : class
ReuseReplaces code repetition across data typesList<T>, Dictionary<K,V>

πŸ§ͺ Challenge Task

Create a class Pair<T1, T2> that:

  • Stores two values of any type
  • Has a method Describe() that returns a string like:
    "Pair contains: [First: 123, Second: Hello]"

Then try it with:

Pair<int, string> combo = new Pair<int, string>(123, "Hello");
combo.Describe();

βœ… Best Practices

βœ… Name generic parameters T, TKey, TValue, TResult, etc.
βœ… Use constraints to guide generic usage
βœ… Avoid boxing/unboxing by using generics with value types
βœ… Keep generics lean and focused β€” don’t over-engineer
βœ… Rely on List<T>, Dictionary<K,V>, and similar .NET generics when possible


πŸ“˜ Want to see how generics integrate with LINQ, Entity Framework, or Web APIs? Our advanced C# development course walks through dozens of real-world examples using generic collections and models.