【问题标题】:Why constrain to interface in generic typing?为什么要在泛型类型中限制接口?
【发布时间】:2016-01-25 18:43:38
【问题描述】:

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

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

直接继承 IProcessor 作为泛型没有任何约束不是更好吗?这样做有什么好处?

例如,

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

【问题讨论】:

  • 这是一个相当少见的用法。更常见的是看到类似public class ProcessorHandler&lt;in T&gt; : IHandler&lt;in T&gt; where T : IProcessor
  • 我不太明白你所说的“继承 IProcessor 作为泛型”是什么意思。你能解释一下吗?
  • 公共类测试:IProcessor
  • IProcessor 包含什么?此接口是否仅用于将对象识别为处理器?
  • 它将包含:void Handle(T command);

标签: c# generics constraints covariance contravariance


【解决方案1】:

如果界面为空白,则为marker interface

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


一个非常正当的理由是在应用装饰器时:

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

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

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

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);
    }
}

【讨论】:

  • 是的,如果它是一个具体的类并且 IProcessor 声明了成员,我就会明白。但是,我正在使用一个已经完成的项目,我只是想维护代码。话虽如此——在我的项目中,接口 IProcessor 是空的,而 Handler 是一个接口(没什么大不了的)。如果 IProcessor 为空,将其设为约束有什么好处?
  • 在这种情况下是marker constraint
【解决方案2】:

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

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

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; }
}

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 2018-07-23
    • 1970-01-01
    • 1970-01-01
    • 2017-07-30
    • 1970-01-01
    • 1970-01-01
    • 2012-07-19
    • 1970-01-01
    • 2023-02-21
    相关资源
    最近更新 更多