Understanding Generics in C# 2.0: Implementation and Usage

Generics provide a mechanism to create reusable code components that work with any data type while maintaining type safety. Instead of writing separate logic for integers, strings, or custom objects, a single generic definition can handle them all.

Consider a utility that processes collections of elements. Without generics, you might rely on object types, leading to performance overhead due to boxing and unboxing. With generics, the structure remains type-safe and efficeint.

Generic Method Example

The folowing DisplayItems method accepts an array of any type T. The type parameter T acts as a placeholder that is substituted with a specific type (like int or string) when the method is called.

using System;

public class GenericDemo
{
    public static void Main(string[] args)
    {
        // Initialize a collection of integers
        int[] numbers = { 10, 20, 30 };
        DisplayItems(numbers);

        // Initialize a collection of strings
        string[] words = { "CSharp", "Generics", "Demo" };
        DisplayItems(words);
    }

    // Generic method definition
    public static void DisplayItems<T>(T[] inputArray)
    {
        foreach (T item in inputArray)
        {
            Console.WriteLine(item);
        }
        Console.WriteLine("---");
    }
}

Implementation Mechanics

When the C# compiler encounters a generic definition (such as a class or method), it produces IL (Intermediate Language) code that contains placeholders for the type arguments. The Common Language Runtime (CLR) handles the specialization of these types at the point of use.

For instance, defining a custom container class DataHolder<T>:

public class DataHolder<T>
{
    private T[] _storage;

    public DataHolder(int size)
    {
        _storage = new T[size];
    }

    public void Insert(T value, int index)
    {
        if (index >= 0 && index < _storage.Length)
        {
            _storage[index] = value;
        }
    }
}

Contrary to the misconception that the compiler generates a separate non-generic class using object for every generic definition, the CLR actually creates specific native code for each value type used (e.g., DataHolder<int> generates different code then DataHolder<long>) to avoid boxing. For reference types (like string), the CLR shares a single native code implementation because references are the same size, ensuring memory efficiency and high performance.

Tags: C# generics C# 2.0 software development .NET

Posted on Thu, 13 Aug 2026 16:40:50 +0000 by r270ba