Monday, June 22, 2026

C# Closed generic type Vs. Open generic type

Learning points:
  • Type parameter
  • Type argument
  • Type constraint
  • Open generic
  • Closed generic

Let us consider the two generic interface cases:
  • IComparer<Doctor> 
  • IComparer<T> 
where Doctor is a class and T is a type

Both are related to generics, but they refer to different things.
  • IComparer<T> → generic interface definition (open generic type)
  • IComparer<Doctor> → constructed generic type (closed generic type / closed constructed type)
Example:
public interface IComparer<T>
{
    int Compare(T x, T y);
}
Here:
  • T → type parameter
  • IComparer<T> → open generic interface because T is not replaced yet

When you substitute T:
  • IComparer<Doctor>
Now:
  • Doctor → type argument
  • IComparer<Doctor> → closed generic interface (generic type with actual type supplied)
Example:
class DoctorNameComparer : IComparer<Doctor>
{
    public int Compare(Doctor x, Doctor y)
    {
        return string.Compare(x.Name, y.Name);
    }
}
Terminology 
Syntax Name
IComparer<T> Generic interface / Open generic type
T Type parameter
IComparer<Doctor> Closed generic type / Constructed generic type
Doctor Type argument

Note that where constraints are used only with type parameters, so they apply to open generic declarations, not to closed generic types.

Example of a generic constraint:
class Repository<T> where T : class { }
Here:
  • T → type parameter
  • where T : class → constraint on T
  • Repository<T> → open generic type
Then you create closed generic types:
Repository<Employee> repo1;   // OK
Repository<int> repo2;        // Error (int is not class)
The constraint is checked when closing the generic type.
You cannot write:
Repository<Employee> where Employee : class // Invalid
because Employee is already a concrete type argument, not a type parameter.

Another example:
void Process<T>() where T : IDisposable
{
}
Process<FileStream>(); // Valid syntax:
Process<FileStream>() where FileStream : IDisposable // Invalid syntax:

Rule
  • where → only for type parameters (T, TKey, TValue, etc.)
  • Never for actual types (Doctor, Employee, string, etc.)
So constraints belong to the generic definition, not to the closed generic instance.

Generic type constraints are specified using the where keyword only.
When people say generic constraints (restricting what T can be), C# syntax uses where keyword.
Examples:
class Repository<T> where T : class
{
}
class Factory<T> where T : new()
{
}
class Service<T> where T : IDisposable
{
}
class Pair<TKey, TValue>
    where TKey : notnull
    where TValue : class
{
}
Common constraints:
Constraint Meaning
where T : class Reference type
where T : struct Value type
where T : unmanaged Unmanaged type
where T : notnull Non-nullable (reference + value) type
where T : new() explicit public parameterless constructor
where T : BaseClass Must inherit from base class
where T : IInterface Must implement interface
where T : U Must derive from another type parameter

Info. All value types automatically have a parameterless constructor.

No comments:

Post a Comment

Hot Topics