C# Generics: An Introduction

Understanding C# Generics

Generics provide a way to create type-safe collections and methods that can operate on a variety of types without casting. This significantly improves performance and code reusability.

What are Generics?

Before generics, collections like ArrayList in C# could store objects of any type. However, retrieving an element required casting it back to its original type, which was prone to runtime errors. Generics solve this by allowing you to specify the type of data a collection or method can hold at compile time.

Benefits of Using Generics

Generic Classes

A generic class is a class that is parameterized by types. You define a generic class by appending type parameters in angle brackets (<T>) to the class name. The T is a placeholder for a type that will be specified when an instance of the class is created.

Example: A Simple Generic List


public class GenericList<T>
{
    private T[] _items;
    private int _count;

    public GenericList(int capacity = 10)
    {
        _items = new T[capacity];
        _count = 0;
    }

    public void Add(T item)
    {
        if (_count == _items.Length)
        {
            // Resize array if necessary (simplified for example)
            System.Array.Resize(ref _items, _items.Length * 2);
        }
        _items[_count++] = item;
    }

    public T this[int index]
    {
        get { return _items[index]; }
        set { _items[index] = value; }
    }

    public int Count
    {
        get { return _count; }
    }
}
                

Now, you can create instances of GenericList for specific types:

Using the Generic List


// A list of integers
GenericList<int> numberList = new GenericList<int>();
numberList.Add(10);
numberList.Add(20);
int firstNumber = numberList[0]; // No casting needed

// A list of strings
GenericList<string> stringList = new GenericList<string>();
stringList.Add("Hello");
stringList.Add("World");
string s = stringList[1]; // No casting needed
                

Generic Methods

Methods can also be generic. This is useful for operations that are generic in nature, regardless of the class they are defined in.

Example: A Generic Swap Method


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

Usage:

Using the Generic Swap Method


int x = 5, y = 10;
GenericUtility.Swap<int>(ref x, ref y); // x is now 10, y is now 5

string s1 = "Apple", s2 = "Banana";
GenericUtility.Swap<string>(ref s1, ref s2); // s1 is now "Banana", s2 is now "Apple"
                

In many cases, the compiler can infer the type argument, so you don't need to explicitly specify it:


GenericUtility.Swap(ref x, ref y); // Compiler infers T as int
GenericUtility.Swap(ref s1, ref s2); // Compiler infers T as string
            

Built-in Generic Collections

The .NET Framework provides a rich set of generic collection classes in the System.Collections.Generic namespace, such as:

Conclusion

Generics are a fundamental feature in C# that significantly enhances type safety and performance. By mastering generic classes and methods, you can write more robust, efficient, and reusable code.