How to implement a common method with restrictions

from my name it can be a little difficult to understand what I'm trying to achieve, so I'll tell you more.

I have the following interface:

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

Now I want to implement the interface in my actual builder. The creator that I use to map various types of objects. So it looks like this:

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

Now the problem is as follows. I can not get the restriction to work. Since I defined a restriction on the interface, it will not allow me to use another restriction for my actual method, although my BusinessModel inherits from BaseModel. He keeps telling me that my M constraint must match the constraint from the interface. I tried several different approaches, but no one works.

Does anyone know if and how this can be achieved? I just want to say that my interface restriction allows for legacy models.

+4
source share
4 answers

Here is a short but complete example of your problem:

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

This will not compile for the same reason as yours. An implementation of an interface method must have the same restriction for a common argument; it cannot have a more restrictive restriction.

Your Build method can only restrict the BaseModel type, not the BussinessModel .

If you change your IModelBuilder interface to add an additional argument to the general class, and then use it as a constraint, you can get the desired functionality:

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

This solution is based in part on the Reed answer , but it takes another step.

+4
source

You need to put both as class constraints:

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

Then you can use:

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

You can define your interface a little differently to enable, for example.

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

and use it as below

 public class BusinessModelBuilder : IModelBuilder<IBusinessTemplate,BusinessModelBuilder> 
0
source

This is because the restriction is really different, so it does not satisfy the interface.

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

The compiler will expect the Build arguments to be BaseModel , but since you may have a type that inherits from BaseModel , which is not a BusinessModel , this is obviously not valid.

Now, to get to the actual meat of the problem, you can solve it with another general argument.

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

Alternatively, you can switch amongst yourself and get some kind of general type of output if you really want to go crazy (if that even applies to your argument):

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

Then you can simply create the various ModelBinder classes and link them in the factory.

Call example:

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

Source: https://habr.com/ru/post/1496201/


All Articles