如何实现带约束的泛型方法

本文关键字:约束 泛型方法 实现 何实现 | 更新日期: 2023-09-27 18:27:08

从我的标题来看,可能有点难以理解我想要实现的目标,所以我将更详细地介绍。

我有以下接口:

public interface IModelBuilder<T>
    where T : IStandardTemplateTemplate
{
    M Build<M>(T pTemplate, params object[] pParams) where M : BaseModel;
}

现在我想在实际的构建器中实现这个接口。我用来映射不同对象类型的生成器。因此,这看起来如下:

public class BusinessModelBuilder : IModelBuilder<IBusinessTemplate>
{
    public virtual M Build<M>(IBusinessTemplate pTemplate, params object[] pParams) where M : BussinessModel
    {
        var businessModel = Activator.CreateInstance<M>();
        // map data
        return businessModel;
    }
}

现在的问题如下。我无法获得工作的约束。由于我在接口上定义了约束,所以它不允许我在实际方法上使用不同的约束,即使我的BusinessModel继承自BaseModel。它一直告诉我,我的约束M必须与接口中的约束相匹配。我尝试了几种不同的方法,但似乎都不起作用。

有人知道这是否或如何实现吗?我只想告诉接口中的约束,允许继承模型。

如何实现带约束的泛型方法

下面是一个简短但完整的问题示例:

public class Parent { }
public class Child { }
public interface Interface
{
    void Foo<T>() where T : Parent;
}
public class Implementation : Interface
{
    public void Foo<T>() where T : Child
    {
    }
}

这不会编译,原因与您的原因相同。接口方法的实现必须对泛型参数具有完全相同的约束,不能有更严格的约束。

Build方法只能将类型约束为BaseModel,而不能约束为BussinessModel

如果您更改IModelBuilder接口以添加额外的类级通用参数,然后使用作为约束,则可以获得所需的功能:

public interface IModelBuilder<T, Model>
    where T : IStandardTemplateTemplate
    where Model : BaseModel
{
    M Build<M>(T pTemplate, params object[] pParams) where M : Model;
}    
public class BusinessModelBuilder : IModelBuilder<IBusinessTemplate, BussinessModel>
{
    public virtual M Build<M>(IBusinessTemplate pTemplate, params object[] pParams)
        where M : BussinessModel
    {
        var businessModel = Activator.CreateInstance<M>();
        // map data
        return businessModel;
    }
}

这个解决方案在一定程度上是基于里德的回答,但它更进一步。

您需要将两者都作为类约束:

public interface IModelBuilder<T, M>
    where T : IStandardTemplateTemplate
    where M : BaseModel
{
    M Build<M>(T pTemplate, params object[] pParams);
}

然后您可以使用:

public class BusinessModelBuilder : IModelBuilder<IBusinessTemplate, BusinessModel>
{
   public virtual BusinessModel Build(IBusinessTemplate pTemplate, params object[] pParams)
   {
       //...

您可以稍微不同地定义您的接口,以包括例如

public interface IModelBuilder<T,M>
    where T : IStandardTemplateTemplate
    where M: BaseModel
{
    M Build<M>(T pTemplate, params object[] pParams);
}

并像下面那样使用它

public class BusinessModelBuilder : IModelBuilder<IBusinessTemplate,BusinessModelBuilder>

这是因为约束实际上是不同的,所以它不满足接口。

IModelBuilder<IBusinessTemplate> sample = new BusinessModelBuilder();
sample.Build(??) 

编译器希望Build的参数是BaseModel,但由于可以有一个从BaseModel继承的类型不是BusinessModel,因此这显然是无效的。

现在,为了了解问题的实质,您可以使用另一个通用论点来解决这个问题。

 public interface IModelBuilder<TemplateType, ConstraintType>
 {
     public ConstraintType Build<ConstraintType>(TemplateType template, params object[] parameters);
 }

或者,如果你真的想疯掉(假设这甚至适用于你的论点),你可以切换一下,得到一些通用的类型推断:

public interface IStandardTemplate<TModel> { }
public interface IModelBinder<TModel>
{
     TModel ApplyParameters(IStandardTemplate<TModel> template, params object[] parameters);
}
public class ModelBuilder
{
      public TModel Build<TModel>(IStandardTemplate<TModel> template, params object[] parameters)
      {
          var model = Activator.CreateInstance<TModel>();
          var modelBinder = ModelBinderFactory.CreateBinderFor(model);
          return modelBinder.ApplyParameters(template, parameters);
      }
}

然后,您可以简单地创建各种ModelBinder类,并在工厂中将它们关联起来。

示例调用:

  public class BusinessTemplate : IStandardTemplate<BusinessModel> { }
  var businessTemplate = new BusinessTemplate();
  var model = new ModelBinder().Build(businessTemplate); // model is of type 'BusinessModel'