Generics in C#: Complete Guide to Usage and Benefits

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Generics in C#: Why They Are Needed and How to Use Them



Generic types (Generics) are one of the key features of the C# language, allowing you to create classes, methods, and interfaces with parameterized types. Instead of writing the same code for each data type, you write it once and specify the type when using it. This improves performance, type safety, and code reusability. In this article, we will look at why Generics are needed, how to apply them in practice, and examine real examples from .NET.



Why Generics Are Needed in C#



Before the introduction of Generics in C# (version 2.0), developers often used the object type and ArrayList collections. This led to two problems:



  • Performance loss — boxing and unboxing for value types (int, double, struct).
  • Lack of compile-time type checking — you could accidentally put a string into a list of numbers and only get an error at runtime.


Generics solve both problems. They allow you to create type-safe data structures without unnecessary overhead. For example, List<int> works with int without boxing, and the compiler will not allow you to add a string to it.



Basic Concepts of Generics



Generic Classes



A generic class is declared with a type parameter in angle brackets. The type parameter is a placeholder that is replaced with a real type when creating an instance.



public class Box<T>{    private T _value;

public Box(T value) { _value = value; }

public T GetValue() => _value;}


Usage:



var intBox = new Box<int>(42);var stringBox = new Box<string>("Hello");

Console.WriteLine(intBox.GetValue()); // 42Console.WriteLine(stringBox.GetValue());// Hello


Generic Methods



A method can have its own type parameter, even if the class is not generic:



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

// Usageint x = 1, y = 2;Utils.Swap(ref x, ref y);Console.WriteLine($"{x} {y}"); // 2 1


Generic Interfaces



Interfaces with a type parameter allow you to create flexible abstractions:



public interface IRepository<T>{    T GetById(int id);    void Add(T entity);}

public class UserRepository : IRepository<User>{ public User GetById(int id) => new User(); public void Add(User user) { }}


Constraints in Generics



Sometimes you need to impose restrictions on the types that can be substituted. The where keyword is used for this.



Main Types of Constraints



  • where T : class — only reference types (string, classes).
  • where T : struct — only value types (int, DateTime, struct).
  • where T : new() — the type must have a public parameterless constructor.
  • where T : SomeBaseClass — the type must be or inherit from the specified class.
  • where T : ISomeInterface — the type must implement the interface.


Example with multiple constraints:



public class Repository<T> where T : class, IEntity, new(){    public T Create()    {        return new T(); // allowed thanks to new()    }}


Covariance and Contravariance in Generics



Starting with C# 4.0, it became possible to manage the variance of generic interfaces using the in and out keywords.



  • Covariance (out) — allows us

Blogs

Book Recommendations