5

私のタイトルからは、私が達成しようとしていることを理解するのが少し難しいかもしれないので、もう少し詳しく説明します.

私は次のインターフェースを持っています:

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 はインターフェイスからの制約と一致する必要があると私に言い続けます。いくつかの異なるアプローチを試しましたが、どれもうまくいかないようです。

これを達成できるかどうか、またはその方法を知っている人はいますか? 継承されたモデルが許可されていることをインターフェイスで制約に伝えたいだけです。

4

4 に答える 4

5

問題の短いが完全な例を次に示します。

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メソッドは型を にのみ制約でき、 には制約できませBaseModelBussinessModel

インターフェイスを変更して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;
    }
}

このソリューションは、リードの回答に部分的に基づいていますが、さらに一歩進んでいます。

于 2013-08-09T17:34:05.523 に答える
2

両方をクラス制約として配置する必要があります。

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)
   {
       //...
于 2013-08-09T17:31:19.123 に答える
0

たとえば、インターフェイスを少し異なる方法で定義して、含めることができます

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>
于 2013-08-09T17:34:00.690 に答える
0

これは、実際には制約が異なるため、インターフェイスを満たしていないためです。

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

コンパイラは、引数が であることを期待しますBuildが、 ではないBaseModel型を継承することができるため、これは明らかに無効です。BaseModelBusinessModel

ここで、問題の本質に到達するために、別の一般的な引数でこれを解決できます。

 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'
于 2013-08-09T17:46:09.503 に答える