0

TL;DR:

ジェネリックを使用せず、newキーワードを使用せずに、派生クラスがメソッドの戻り値の型をオーバーライドできるようにする抽象メソッドを基本クラスに追加する方法はありますか?


LLBLGen Pro 用のカスタム テンプレートの開発に取り組んでいます。その過程で、LLBLGen Pro が提供するデフォルトのテンプレートを変更することを拒否します。そのため、他の人が私のテンプレートを実装することを選択した場合に、私のアプローチによって他の人のファイルが上書きされることはありません。

私が取り組み始めた (そして順調に進んでいる) タスクの 1 つは、エンティティごとに DTO を生成するテンプレートを開発することです。これらの方針に沿って、目的の 1 つは私のエンティティにToDTO()メソッドを提供することです。ジェネリック プログラミングの観点から、共通の基底クラス内でこのメソッドを定義することにしました。ここから問題が始まります。


基本クラスでメソッドを定義する目的は、特定のエンティティではなく、 で作業したいToDTO()汎用リポジトリ (メソッドなどを使用) を作成しようとしているからです。 .Fetch()CommonEntityBase


LLBLGen はそのCommonEntityBaseクラスを次のように定義します。

public abstract partial class CommonEntityBase : EntityBase2 {
   // LLBLGen-generated code
}

私の当初の計画は、私のメソッドを次のように別の部分クラスに追加することでした:

public abstract partial class CommonEntityBase {
    public abstract CommonDTOBase ToDto();
}

継承されたクラスは、次のように、メソッドで戻り値の型を基本クラスの戻り値の型から派生した型として定義できると思いました。

public partial class PersonEntity : CommonEntityBase {
    public override PersonDTO ToDto(){ return new PersonDTO(); }
}

しかし、私は間違っていました。


私の2番目の試みは、ジェネリックを使用してクラスを定義することでした。

public abstract partial class CommonEntityBase<T> : CommonEntityBase 
      where T : CommonDTOBase {
   public abstract T ToDto();
}

十分に単純です。私がしなければならないことは、生成されたエンティティ クラスがこの新しいエンティティ ベースから継承されるようにすることだけです。1つだけ注意してください。LLBLGen のテンプレートを上書きしたくないので、部分クラスに戻ります。

LLBLGen の個々のエンティティには、次の定義があります。

public partial class PersonEntity : CommonEntityBase {
   // LLBLGen-generated code
}

ここに私の問題があります。メソッドを機能させるには、次の定義で独自の部分クラスを作成する必要があります。

public partial class PersonEntity : CommonEntityBase<PersonDTO> {
   public override PersonDTO ToDto(){ return new PersonDTO(); }
}

もちろん、これは不可能です。 なぜなら、私が今知っているように、

基本クラスを指定する [部分クラスの] 部分はすべて一致する必要がありますが、基本クラスを省略した部分は依然として基本型を継承します。


3 番目に試みたのは、基本クラスの関数定義をnewキーワードで単純にオーバーライドすることでした。

public abstract partial class CommonEntityBase {
    public virtual CommonDTOBase ToDto(){ return null; }
}

public partial class PersonEntity : CommonEntityBase {
    public new PersonDTO ToDto(){ return new PersonDTO(); }
}

ただし、 としてキャストされたときにPersonEntityのメソッドにアクセスできるようにしたいので、これは私のアプローチの目的を完全に無効にします。このアプローチでは、次のことを行います。ToDTO()CommonEntityBase

CommonEntityBase e = new PersonEntity();
var dto = e.ToDto();

null になってしまいdtoますが、これは望ましくありません。


私の最初のアプローチと、それがうまくいかない理由について議論しているさまざまな リンクに出くわしました。通常、一般的な意味での解決策として私の一般的なアプローチを指しています。ただし、私の状況では、ジェネリックは機能していないようです。


これはすべて、私が達成しようとしていることが可能かどうかを尋ねるためのものです。

ジェネリックを使用せず、newキーワードを使用せずに、派生クラスがメソッドの戻り値の型をオーバーライドできるようにする抽象メソッドを基本クラスに追加する方法はありますか?

それとも、間違った角度からこれに取り組んでいるのかもしれません。私の問題を解決できる他のテクニックがありますか?


編集

ポージェスのアプローチを採用して、エンティティで達成したいことのユースケースを次に示します。

public class BaseRepository<D,E> where D : CommonDTOBase where E : CommonEntityBase,new
    public D Get(Guid id){
        var entity = new E();
        entity.SetId(id);

        // LLBLGen adapter method; populates the entity with results from the database
        FetchEntity(entity);

        // Fails, as entity.ToDto() returns CommonDTOBase, not derived type D
        return entity.ToDto();
    }
}
4

2 に答える 2

4

それ以外の:

public abstract partial class CommonEntityBase {
    public abstract CommonDTOBase ToDto();
}

public partial class PersonEntity : CommonEntityBase {
    public override PersonDTO ToDto(){ return new PersonDTO(); }
}

次のような DTO を返すだけではないのはなぜですか。

public abstract partial class CommonEntityBase {
    public abstract CommonDTOBase ToDto();
}

public partial class PersonEntity : CommonEntityBase {
    // changed PersonDTO to CommonDTOBase
    public override CommonDTOBase ToDto(){ return new PersonDTO(); }
}

オブジェクト指向コードのほうが慣用的だと思います。DTO の正確な型を知る必要がある理由はありますか?

于 2010-06-27T03:33:39.253 に答える
0

LLBLGen についてはわかりませんが、型パラメーターを保持するインターフェイスを導入することで、この方法で問題を解決できると思います。

public interface DTOProvider<T> where T : CommonDTOBase {
    public T ToDTO();
}

そして、エンティティ クラスに対して、次のようにします。

public partial class PersonEntity : CommonEntityBase, DTOProvider<PersonDTO> {
    public PersonDTO ToDto() { return new PersonDTO(); }
}

部分クラスはさまざまなインターフェイスを導入できるため、これは機能します。唯一の悲しい点は、基本型を介してメソッドにアクセスするにはキャストが必要なことです。

public void DoSomethingWithDTO<T>(CommonBaseEntity entity)
        where T : CommonDTOBase {
    T dto = ((DTOProvider<T>) entity).ToDTO();
    ...
}

もちろん、エンティティ派生型のいずれかの参照がある場合は、キャストなしで ToDTO を直接呼び出すことができます。

public void DoSomethingWithPersonDTO(PersonEntity entity)
{
    PersonDTO dto = entity.ToDTO();
    ...
}

.NET Framework 4 を使用している場合は、ジェネリック バリアンスを使用して、DTO 型の共変を宣言することにより、CommonDTOBase の操作だけを行うコードから DTOProvider インターフェイスを使いやすくすることができます。

public interface DTOProvider<out T> where T : CommonDTOBase {
    public T ToDTO();
}

(「out」に注意してください。) 次に、DoSomethingWithDTO メソッドは型パラメーターを必要としません。

public void DoSomethingWithDTO(CommonBaseEntity entity) {
    CommonDTOBase dto = ((DTOProvider<CommonDTOBase>) entity).ToDTO();
    ...
}

: CommonBaseEntity, DTOProvider<T>CommonBaseEntity 部分クラスで試して宣言するのは魅力的です。残念ながら、部分定義がマージされると型パラメーターが引き継がれ、 CommonBaseEntity 型がジェネリック型になるため、これは機能しません。

于 2010-06-26T23:35:49.320 に答える