8

継承されたクラスでコードを繰り返す必要がないように、デフォルトのコンストラクター機能を提供するジェネリック抽象クラスがあるとします。

public abstract class Base<TKey, TValue>
{
    private static readonly IDictionary<TKey, Base<TKey, TValue>> Values = new Dictionary<TKey, Base<TKey, TValue>>();

    public TKey Key { get; private set; }
    public TValue Value { get; private set; }

    protected Base(TKey key, TValue value)
    {
        this.Key = key;
        this.Value = value;

        Values.Add(key, this);
    }
}

問題は、次のような 2 つの継承されたクラスを作成する場合です。

public sealed class One : Base<string, string>
{
    public static readonly One Default = new One("DEF", "Default value");
    public static readonly One Invalid = new One("", "Invalid value");

    private One(string key, string value) : base(key, value) {}
}

public sealed class Two : Base<string, string>
{
    public static readonly Two EU = new Two("EU", "European Union");
    public static readonly Two USA = new Two("", "United States of America");

    private Two(string key, string value) : base(key, value) {}
}

ご覧のとおり、これら 2 つはタイプ セーフな列挙型です。定義済みの値のみを提供し、他の目的でインスタンス化することはできません。

質問

問題は、これらの継承されたクラスの両方が、基本クラスである同じジェネリック型を使用しているためですBase<string, string>。また、ジェネリック クラスと静的フィールドが機能する方法は、ジェネリック型 (または複数の場合は組み合わせ) ごとに新しい静的フィールドが作成されることです。

私の場合、組み合わせは、基本クラスの静的フィールドが1つstring, stringしかなく、それぞれ2つの値を持つ2つの静的フィールドではなく、4つの値を保持する理由です。

コードを繰り返さないように、基本クラスでこの機能を維持しながら、この問題を克服してそれらを分離するにはどうすればよいですか?

フィールドを静的からインスタンスに変更しても機能しません。それぞれが 1 つの値しか保持しない 4 つのインスタンスになってしまうためです...

4

5 に答える 5

9

これは機能します。これは奇妙に繰り返されるテンプレート パターンとして知られていると思いますが、それについては引用しないでください。

public abstract class Base<TSelf, TKey, TValue>
{
    private static readonly IDictionary<TKey, Base<TSelf, TKey, TValue>> Values = 
        new Dictionary<TKey, Base<TSelf, TKey, TValue>>();

    public TKey Key { get; private set; }
    public TValue Value { get; private set; }

    protected Base(TKey key, TValue value)
    {
        this.Key = key;
        this.Value = value;

        Values.Add(key, this);
    }
}


public sealed class One : Base<One, string, string>
{
    public static readonly One Default = new One("DEF", "Default value");
    public static readonly One Invalid = new One("", "Invalid value");

    private One(string key, string value) : base(key, value) { }
}

public sealed class Two : Base<Two, string, string>
{
    public static readonly Two EU = new Two("EU", "European Union");
    public static readonly Two USA = new Two("", "United States of America");

    private Two(string key, string value) : base(key, value) { }
}

「興味深い」部分は、 と の定義がOneそれTwo自体を型パラメータとして使用できることです!

于 2012-06-19T15:30:11.447 に答える
6

もちろん、問題が発生するのは、2 つのクラスが同じジェネリック型引数を持つ同じ基本クラスを共有しているからです。これは、同じ静的メンバーを共有することを意味します。

コードを繰り返さないように、基本クラスでこの機能を維持しながら、この問題を克服してそれらを分離するにはどうすればよいですか?

1 つの方法は、現在のランタイム タイプに基づいてディクショナリ内にディクショナリを構築することです。

private static readonly IDictionary<Type, IDictionary<TKey, Base<TKey, TValue>>> Values = new Dictionary<Type, IDictionary<TKey, Base<TKey, TValue>>()>;


protected Base(TKey key, TValue value)
{
    this.Key = key;
    this.Value = value;

    IDictionary<TKey, Base<TKey, TValue>> dict;
    if (!Values.TryGetValue(this.GetType(), out dict)
    { 
        dict = new Dictionary<TKey, Base<TKey, TValue>>();
        Values[this.GetType()] = dict;
    }

    dict.Add(key, this);
}

これにより、現在の型を検索してからその辞書内の値を検索する必要があるため、すべての検索が 2 フェーズになりますが、サブクラス API の変更にはまったく依存しません。

于 2012-06-19T15:33:36.203 に答える
2

これは質問に直接答えるものではありませんが、代わりに同じ目的を達成するための代替設計を提供します。

基本的な動作ではない、または少なくとも静的な動作ではないものに基本クラスを使用しようとしているように思えます。

ルックアップ リストアイテムであることと、ルックアップ リスト参照を提供することの懸念を分離します。

既知のルックアップを提供しようとしている場合は、ルックアップ リストの参照をルックアップ アイテムとは別のものにします (静的メンバーではありません)。

public static class MyLookups
{
    public static IDictionary<string, string> Acronyms = new Dictionary<string, string>();
    public static IDictionary<string, string> DefaultValues = new Dictionary<string, string>();
}

次に、各タイプは単に関連する辞書を指すか、名前を付けてリフレクションを介してアクセスするか、それに Func を提供します。

public class One : Base<string, string>
{
    public One(string key, string value)
        : base(key, value, () => MyLookups.DefaultValues)
    {
    }
}

public class Two : Base<string, string>
{
    public Two(string key, string value)
        : base(key, value, () => MyLookups.Acronyms)
    {
    }
}

public abstract class Base<TKey, TValue>
{
    private IDictionary<TKey, TValue> _dictionaryReference;

    protected Base(TKey key, TValue value, Func<IDictionary<TKey, TValue>> getDictionary) 
    {
        _dictionaryReference = getDictionary();
        _dictionaryReference.Add(key, value);
    }
}

これにより、リスト自体をリスト内のアイテムから分離することもできます (おそらくインジェクションを介して)。

また、基本クラスのインスタンスが同じ辞書への参照を保持している場合、メンバーを静的にする必要がないことに注意してください。

于 2012-06-19T15:43:55.260 に答える
0

あなたのベースを違うものにしましょう。

public abstract class Base<T, TKey, TValue> where T : Base<T, TKey, TValue>
{ 
    private static readonly IDictionary<TKey, T> Values = 
        new Dictionary<TKey, T>(); 

    public TKey Key { get; private set; } 
    public TValue Value { get; private set; } 

    protected Base(TKey key, TValue value) 
    { 
        this.Key = key; 
        this.Value = value; 

        Values.Add(key, (T)this); 
    } 
} 

更新:投稿後、多かれ少なかれ同じ回答が既に与えられていることに気付きましたが、違いに注意してください:型 T の制約と、辞書定義での T の使用。それはばかげた証拠ではありませんが、役に立ちます。

オブジェクトをディクショナリに追加できるようにするには、T へのキャストが必要です。as演算子ではなく、明示的なキャストを使用していることに注意してください。誤って使用すると、asオペレーターは null をディクショナリに挿入し、そこでキャストによって (本来あるべきように) 壊れてしまいます。

しかし、以下も参照してください: http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx

于 2012-06-19T15:32:59.010 に答える
0

これは仕様によるものです。静的メンバーはすべてのインスタンスで 1 つのコピーのみを持ち、基本クラスの静的メンバーはそのすべてのサブクラスのすべてのインスタンスで 1 つのコピーしか持ちません。

なぜあなたは静的ですか?One のすべてのインスタンスで共通の辞書を共有しますか?

One を One 間で共有したいが、Two とは共有したくない場合は、デザインを変更する必要があります。静的にしない (共有が発生しない) か、各サブクラスのレベルで静的にします。

基本クラスで抽象メソッド/プロパティを作成し、それらの間で API を同じに保ちたい場合はサブクラスでオーバーライドします

于 2012-06-19T15:34:44.860 に答える