Post

C# | Understanding Generics

Generics in C# provide a way to write flexible and reusable code by allowing the creation of classes, structures, interfaces, and methods with placeholders for data types. This enables the writing of code that works with any data type without sacrificing type safety.

Generic Classes

A generic class is a template that can work with any data type. Here’s a simple example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Box<T>
{
    private T value;

    public void Set(T newValue)
    {
        value = newValue;
    }

    public T Get()
    {
        return value;
    }
}

// Usage
Box<int> intBox = new Box<int>();
intBox.Set(42);
int intValue = intBox.Get();

Box<string> stringBox = new Box<string>();
stringBox.Set("Hello, Generics!");
string stringValue = stringBox.Get();

In this example, the Box class is generic with the type parameter T. This allows creating instances of Box with different data types.

Generic Methods

Generic methods allow specifying the data type at the method level:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Utility
{
    public static void Swap<T>(ref T a, ref T b)
    {
        T temp = a;
        a = b;
        b = temp;
    }
}

// Usage
int num1 = 5, num2 = 10;
Utility.Swap(ref num1, ref num2);

string str1 = "Hello", str2 = "World";
Utility.Swap(ref str1, ref str2);

In this example, the Swap method can be used to swap the values of variables of any data type.

Constraints

Generics support constraints to restrict the types that can be used. For instance, you might want to ensure that a generic type implements a particular interface:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public interface IShape
{
    double Area();
}

public class ShapeContainer<T> where T : IShape
{
    private T shape;

    public ShapeContainer(T shape)
    {
        this.shape = shape;
    }

    public double GetArea()
    {
        return shape.Area();
    }
}

// Usage
public class Circle : IShape
{
    public double Radius { get; set; }

    public double Area()
    {
        return Math.PI * Radius * Radius;
    }
}

ShapeContainer<Circle> circleContainer = new ShapeContainer<Circle>(new Circle { Radius = 5.0 });
double circleArea = circleContainer.GetArea();

Here, the ShapeContainer class only accepts types that implement the IShape interface.

What Next?

Generics in C# are a powerful tool for creating flexible and reusable code, promoting type safety, and enhancing code maintainability.

This post is licensed under CC BY 4.0 by the author.