4

問題をできるだけ簡単に説明するために、実装している限り、任意の数の具象クラスを処理できる汎用プーリング システムを実装しようとしていますIBaseComponent

したがって、プールを管理するクラスには、プールのディクショナリがあります。

Dictionary<Type, Pool<IBaseComponent>> pools;

これにより、実装するクラスIBaseComponent(いわば非常に「低レベル」のインターフェイスです。したがって、実装するクラスはそれほど互換性がありません) を必要なだけ作成できます。それぞれのプール。

今、私が直面している問題はIBaseComponent、プールへの の最初のロード、いわばテンプレートとして機能することです。

このテンプレート オブジェクトは、コードではなく XML から読み込まれるため、実際のクラスはコンパイル時になく、実行時にしかありません (XML 定義で定義され、リフレクションを介して正式な型を取得します)。ご存知のように、ジェネリックはコンパイル時の安全性に依存していることを除けば、それはすべてうまくいきます。

したがって、いくつかの反射トリックを使用して、次のようにします。

var type = typeof(MyChildComponent);
var genericType = typeof(Pool<>);
var specificType = genericType.MakeGenericType(type);
var pool = Activator.CreateInstance(specificType );

pools.Add(T, pool as Pool<IBaseComponent>);

いくつかのクラスを想定:

public class MyChildComponent : IBaseComponent

問題は、プール ディクショナリに追加しているときに、最初のブロックの最後の行で発生します。インスタンス化されたプールのキャストがPool<IBaseComponent>失敗し、null がディクショナリに挿入されます。

素晴らしい人々への私の質問はこれです:これを回避する合理的な方法はありますか? 可能方法はありますか?

プールの少なくとも最初のテンプレート オブジェクトに対して、プールを使用できる可能性のあるそれぞれの具象クラスに対して、何らかの外部メソッド (XML、TXT など) を介して要素をロードする必要がある場合、アクセスできるのはクラスの最上位インターフェイスと正式な Type (両方とも外部定義ファイルで定義) について、ここで何かできることはありますか?

それとも、これはまったく不可能ですか?

4

2 に答える 2

4

.Net 4+ を使用していますか? その場合は、インターフェイスを作成できますIPool<out T>。はout、ジェネリック引数を共変にします。つまり、T であるか、T から派生したジェネリック引数を持つ任意のバージョンのインターフェイスを受け入れることを意味します。

何らかの理由で、共分散/反変性はインターフェイスとデリゲートでのみ機能するため、IPool が必要です。

辞書は次のようになります。

Dictionary<Type, IPool<IBaseComponent>>() pools;

頭の中で反射と組み合わせるのに少し苦労していますが、うまくいくはずです。そうでない場合はお知らせください。テスト コードにもう少し時間を割きます。

于 2012-11-27T04:45:14.290 に答える
1

私がいじっている代替オプションの 1 つは、Pool<T>それ自体を変更することです。

1 つのタイプの (concerete) クラスのみを格納する代わりにPool<T>、T で使用されるインターフェイスに基づいて互換性のあるクラスを格納できるように変更します。

したがって、Pool<IBaseComponent>は、 を実装するすべての可能な型を格納する責任がありますIBaseComponent

内部的には、すべてを として保存しますIBaseComponentが、各具象型が保存されている場所への参照を保持します ( によってキー付けされたリストの辞書Type、または 1 つの大きな線形リストでも [これにより、特定の「プール」のサイズが変更されます)より複雑な型])

私が言及しなかったことの 1 つIBaseComponentは、「ブラインド」方式で使用するコンポーネントを準備するために必要な機能の 2 つのポイントを公開することです (つまり、このプールを呼び出すファクトリは、コンパイル時にどのタイプかを知りません)。 XMLで定義されているものに基づいてそれらをロードするか、これらのコンポーネントが添付されている既存のオブジェクトからコピーするだけです)、つまり、Deserialize(XML / JSON /何でもからコンポーネントを構築する)およびCopyInto(IBaseComponent other)(コンポーネントを構築する)別のコンポーネントからコピーすることによって)。

したがって、これには、 が呼び出し元の requestedPoolに動的にキャストできないという問題がまだありますが、それは問題ではありません。呼び出し元が事前にハード コンパイル時の型を実際に知っている場合は、キャストを実行できます。呼び出し元がそうしないと、とにかくによって公開されたアクセス メソッドを超えて何もできなくなります。IBaseComponentTypeIBaseComponent

重要なのはIBaseComponentPool戻り値が下にある正しい型であり、これが処理されることだけです。

簡単に言えば、最新のジェネリックを少し切り取り (内部的にはプールは渡された型でのみ機能し、外部的には T をインターフェイスにすることのみを許可します)、それを置き換えると古き良き時代のType受け渡しになります。タイプのプールをインスタンス化するためにリフレクションを内部的に使用する必要がありますが、プールの初期化またはサイズ変更は非常にコストのかかる操作になると予想しても問題ないと思います。

于 2012-11-27T14:53:41.530 に答える