为什么要在泛型类型中限制接口

本文关键字:接口 泛型类型 为什么 | 更新日期: 2023-09-27 18:31:53

C# 中对泛型的接口类型进行约束有什么意义? 例如

public interface IHandler<in T>
    where T : IProcessor
{
    void Handle(T command);
}

简单地将 IProcessor 作为泛型继承而没有任何限制不是更好吗? 这样做有什么好处?

例如

public class FooProcessor : IProcessor<T>
{
    void Handle(T command)
    {
    }
}

为什么要在泛型类型中限制接口

如果接口为空,则它是一个标记接口。

它可用于在类外部应用有关类的限制。为了与下面的示例保持一致,您可以将装饰器限制为只能装饰IProcessor处理程序。


一个非常合理的原因是在应用装饰器时:

假设命令参数接口有几个属性:

public interface IProcessor
{
    int Id { get; }
    DateTime Date { get; }
}

我们可以在IProcessor命令的所有处理程序上定义一个装饰器,这些命令知道所有命令参数都具有IdDate

public sealed class HandlerLogger<in T> where T : IProcessor
{
    private readonly ILogger logger;
    private readonly IHandlerLogger<T> decorated;
    public HandlerLogger(
        ILogger logger,
        IHandlerLogger<T> decorated)
    {
        this.logger = logger;
        this.decorated = decorated;
    }
    public void Handle(T command)
    {
        this.logger.Log(command.Id, command.Date, typeof(T).Name);
        this.decorated.Handle(command);
    }
}

有不同的体系结构模式可用于实际强制实施类型。
例如,如果您正在设计一个 API,并且希望允许某人扩展它,但您希望确保为扩展框架而创建的类是特定类型,并且具有默认的无参数构造函数。 使用泛型类型化接口是执行此操作的常用方法。

我创建了一个快速示例adhoc,以相对简单地概述为什么类型化接口在某些模型/体系结构设计中很有用。

public class UnitOfWorkManager<T>
{
    private readonly IDataRepository _dataRepository;
    private List<T> _unitOfWorkItems;
    public UnitOfWorkManager(IDataRepository dataRepository)
    {
        _dataRepository = dataRepository;
    }
    public void AddUnitOfWork(IUnitOfWork<T> unitOfWork)
    {
        this._unitOfWorkItems.Add(unitOfWork);
    }
    public void Execute()
    {
        WorkerItem previous = null;
        foreach (var item in _unitOfWorkItems)
        {
            var repoItem = _dataRepository.Get(item.Id);
            var input = new WorkerItem(item.Id, repoItem.Name, previous);
            previous = input;
        }
    }
}
public interface IUnitOfWork<T> 
    where T: WorkerItem, new()
{
    string Id { get; }
    void Execute(T input);
}
public class WorkerItem
{
    public WorkerItem(string id, string name, WorkerItem previous)
    {
        this.Name = name;
        this.Id = id;
        this.Previous = previous;
    }
    public string Id { get; private set; }
    public string Name { get; private set; }
    public WorkerItem Previous { get; private set; }
}

希望这有帮助。