Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A generic class in C# is a blueprint that defers the specification of one or more data types until the class is instantiated by client code. By defining a class with type parameters, you create a single, strongly-typed implementation that operates uniformly across different data types without incurring the performance overhead of boxing, unboxing, or runtime type casting.

Syntax and Structure

A generic class is declared by appending angle brackets (<>) containing one or more type parameters to the class name.
public class Container<T>
{
    private T _item;

    public Container(T item)
    {
        _item = item;
    }

    public T GetItem()
    {
        return _item;
    }
}

Core Terminology

  • Type Parameter: The placeholder identifier (e.g., T) defined in the class declaration. It acts as a variable for a type.
  • Type Argument: The concrete data type (e.g., int, string, Customer) supplied by the client code when instantiating the class (e.g., new Container<int>(5)).
  • Unbound Generic Type: A generic type definition with no type arguments specified (e.g., typeof(Container<>)). You cannot instantiate an unbound generic type at runtime (e.g., using Activator.CreateInstance(typeof(Container<>))).
  • Open Constructed Type: A generic type where at least one type argument is an unresolved type parameter (e.g., Container<T> used within the body of a generic class). Open constructed types cannot be instantiated. Although syntax like new Container<T>() is valid within generic source code, object instantiation occurs at runtime. By the time the CLR executes the instantiation, T has been resolved to a concrete type argument, resulting in the instantiation of a closed constructed type.
  • Closed Constructed Type: A generic type where all type parameters have been replaced by concrete type arguments (e.g., typeof(Container<int>)).

Multiple Type Parameters

Generic classes can declare multiple type parameters, separated by commas. Standard naming conventions dictate prefixing type parameters with T followed by a descriptive name.
public class KeyValuePair<TKey, TValue>
{
    public TKey Key { get; set; }
    public TValue Value { get; set; }
}

Type Constraints

By default, an unconstrained type parameter is an unknown, strongly-typed placeholder that merely allows access to the members defined in System.Object. To safely invoke specific methods or enforce structural requirements on the type argument, you apply constraints using the where contextual keyword.
public class DataProcessor<T> where T : class, IDisposable, new()
{
    public void Process()
    {
        // 'new()' constraint allows instantiation
        T instance = new T(); 
        
        // 'IDisposable' constraint allows calling Dispose()
        instance.Dispose();   
    }
}
Common Constraints:
  • where T : struct: Must be a non-nullable value type.
  • where T : class: Must be a reference type.
  • where T : notnull: Must be a non-nullable type (value or reference).
  • where T : new(): Must have a public parameterless constructor.
  • where T : <BaseClass>: Must be or derive from the specified base class.
  • where T : <Interface>: Must implement the specified interface.
  • where T : unmanaged: Must be an unmanaged type (no reference type fields).

Static Members in Generic Classes

Static fields and properties in a generic class are not shared globally across all instances of the generic definition. Instead, they are shared only among instances of the same closed constructed type.
public class StateTracker<T>
{
    public static int InstanceCount;

    public StateTracker()
    {
        InstanceCount++;
    }
}

// Usage mechanics:
new StateTracker<int>();
new StateTracker<int>();
new StateTracker<string>();

// StateTracker<int>.InstanceCount == 2
// StateTracker<string>.InstanceCount == 1
The CLR generates distinct memory layouts and static fields for StateTracker<int> and StateTracker<string>.

Inheritance Mechanics

Generic classes can participate in inheritance hierarchies. A class can inherit from a closed constructed type or propagate its own type parameters to an open constructed base class.
public class BaseNode<T> { }

// 1. Inheriting and closing the generic type
public class IntNode : BaseNode<int> { }

// 2. Inheriting and propagating the type parameter
public class GenericNode<T> : BaseNode<T> { }

// 3. Adding new type parameters while propagating
public class ComplexNode<T, U> : BaseNode<T> { }
When inheriting from a generic base class, any constraints applied to the base class’s type parameters must be duplicated or satisfied by the derived class’s type parameters.
Master C# with Deep Grasping Methodology!Learn More