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 interface in C# is a contract that defines methods, properties, events, or indexers using one or more type parameters (<T>) rather than concrete types. This defers the specification of the actual data types until the interface is implemented or instantiated, ensuring compile-time type safety and eliminating the performance overhead of boxing and unboxing associated with non-generic, object-based interfaces.

Declaration Syntax

Type parameters are declared in angle brackets immediately following the interface name.
public interface IRepository<T>
{
    T Get(int id);
    void Add(T entity);
}
Multiple type parameters can be defined by separating them with commas:
public interface IMapper<TSource, TDestination>
{
    TDestination Map(TSource source);
}

Implementation Mechanics

When a class or struct implements a generic interface, it must resolve the type parameters. This is done using either a closed constructed type or an open constructed type. 1. Closed Constructed Type The implementing class explicitly defines the concrete type for the interface’s type parameter. The class itself does not need to be generic.
public class Customer 
{ 
    // Class members 
}

public class CustomerRepository : IRepository<Customer>
{
    public Customer Get(int id) => new Customer();
    public void Add(Customer entity) { /* Implementation */ }
}
2. Open Constructed Type The implementing class is also generic and passes its own type parameter to the interface, leaving the type unresolved until the class is instantiated.
public class BaseRepository<T> : IRepository<T>
{
    public T Get(int id) => default(T);
    public void Add(T entity) { /* Implementation */ }
}

Type Parameter Constraints

Generic interfaces can enforce rules on the types that can be substituted for type parameters using the where keyword. This ensures the interface can safely invoke specific members or assume specific memory layouts.
using System;

public interface IEntityProcessor<T> where T : class, IDisposable, new()
{
    void Process(T entity);
}
Common constraints include class (reference type), struct (value type), new() (parameterless constructor), and specific base classes or interfaces.

Generic Variance

C# supports generic variance exclusively on interfaces (and delegates). Variance allows for implicit reference conversions for generic interface types based on their type arguments. Important Limitation: Variance applies only to reference types. Value types are always invariant. For example, an IEnumerable<int> cannot be implicitly converted to IEnumerable<object> because int is a value type. Covariance (out keyword) Enables you to use a more derived type than originally specified. The type parameter can be used as a return type of interface methods or as the type of a read-only property (T PropertyName { get; }). It cannot be used as a method argument or a writable property.
public interface IProducer<out T>
{
    T Produce(); 
    T CurrentItem { get; }
    // void Consume(T item); // Compiler error: T cannot be an input
}
Contravariance (in keyword) Enables you to use a less derived (more generic) type than originally specified. The type parameter can be used as a method argument or as the type of a write-only property (T PropertyName { set; }). It cannot be used as a return type or a readable property.
public interface IConsumer<in T>
{
    void Consume(T item);
    T TargetItem { set; }
    // T Produce(); // Compiler error: T cannot be an output
}

Default Interface Methods (C# 8.0+)

Generic interfaces support default implementations. The default method can utilize the type parameter, but its behavior is bound by the constraints applied to that parameter.
using System;

public interface IValidatable<T>
{
    bool IsValid(T item);

    // Default implementation
    void ValidateAndThrow(T item)
    {
        if (!IsValid(item))
        {
            throw new ArgumentException($"Item of type {typeof(T).Name} is invalid.");
        }
    }
}
Master C# with Deep Grasping Methodology!Learn More