web-dev-qa-db-ja.com

C#の場合:キーとして型を持ち、値としてその型のIEnumerable <>を持つ汎用ディクショナリを宣言する方法は?

次のように、特定の型の型指定されたIEnumerableを格納するディクショナリを宣言し、その正確な型をキーとして使用します(johny gのコメントに従うように編集)

private IDictionary<Type, IEnumerable<T>> _dataOfType where T: BaseClass; //does not compile!

保存したい具体的なクラスはすべてBaseClassから派生しているため、制約として使用するという考えです。コンパイラーは、メンバー名の後にセミコロンが必要であることを報告します。

それがうまくいくなら、私はこれが辞書からの後の検索を次のように簡単にすることを期待します:

IEnumerable<ConcreteData> concreteData;
_sitesOfType.TryGetValue(typeof(ConcreteType), out concreteData);

そのような辞書を定義する方法?

24
Marcel

辞書を作成する必要さえないかもしれませんが、それはあなたのニーズ次第です。アプリドメインごとにタイプごとにこのようなリストが1つだけ必要な場合(つまり、「辞書」はstaticです)、次のパターンは効率的であり、型推論をうまく促進します。

interface IBase {}

static class Container {
    static class PerType<T> where T : IBase {
        public static IEnumerable<T> list;
    }

    public static IEnumerable<T> Get<T>() where T : IBase 
        => PerType<T>.list; 

    public static void Set<T>(IEnumerable<T> newlist) where T : IBase 
        => PerType<T>.list = newlist;

    public static IEnumerable<T> GetByExample<T>(T ignoredExample) where T : IBase 
        => Get<T>(); 
}

コンパイル時の型とランタイム型の違いについて、このアプローチを採用する前に注意深く検討する必要があることに注意してください。このメソッドは、ランタイム型のIEnumerable<SomeType>変数はSomeTypeの下と-キャストした場合-SomeTypeを含むIBaseのいずれかの基本型の下で、ランタイムエラーもコンパイルタイプエラーもない-これは機能、またはバグが発生するのを待っているので、ifで確認することができます。

さらに、このアプローチはスレッドを無視します。したがって、複数のスレッドからこのデータ構造にアクセスする場合は、おそらくいくつかのロックを追加する必要があります。参照の読み取り/書き込みはアトミックなので、ロックに失敗しても破損することはありませんが、古いデータや競合状態が発生する可能性があります。

15
Eamon Nerbonne

使用する - System.ComponentModel.Design.ServiceContainer .Netフレームワークですでに利用可能です。

        ServiceContainer container = new ServiceContainer();

        IList<int> integers = new List<int>();
        IList<string> strings = new List<string>();
        IList<double> doubles = new List<double>();

        container.AddService(typeof(IEnumerable<int>), integers);
        container.AddService(typeof(IEnumerable<string>), strings);
        container.AddService(typeof(IEnumerable<double>), doubles);
25

プライベートメンバーでTを制約しません。クラスレベルで制約します。

class Foo<T> where T : BaseClass
{
    private IDictionary<T, IEnumerable<T>> _dataOfType;

    public Foo(IDictionary<T, IEnumerable<T>> dataOfType)
    {
        this._dataOfType = dataOfType;
    }   
}   
11
Mark Rushakoff
  1. 特定の変数を制約することはできません。クラスとメソッドでのみ機能します。正直に言うと、変数レベルではまったく意味がありません。
  2. 必要なのは、カスタムクラス-class WeirdDictionary : IDictionary<Type, IEnumerable>です。これは、Addメソッドをオーバーロードして、Typeとその型のIEnumerableを取得します。これは、制約を使用して実行でき、IEnumerable <>をIEnumerableにキャストできます。同様にインデクサーをオーバーロードし、適切なタイプにキャストします。
    ジェネリックはIEnumerable<Base>IEnumerable<Derived>と同じくらい厳密であるため、このすべてのキャストが必要です(これは分散と呼ばれていると思いますか?)

ロックを再利用するため、このソリューションは少し一般化されています

280Z28で編集:

最初に、ポイント2が混乱し、誤って解釈したため、これをマークダウンしました。 IDictionary<Type, IEnumerable>でメソッドの明示的な実装を使用し、汎用的な代替手段を提供することにより、非常にクリーンなインターフェイスを取得できます。一般的なインデクサーを作成することはできないため、常にTryGet<T>を使用する必要があることに注意してください(これはとにかく良いアイデアです)。チェックの実行方法を示すために、IDictionary<>メソッドの1つの明示的な実装のみを含めました。 WeirdDictionaryDictionary<Type, IEnumerable>から直接派生させないでください。派生データの制約を保証できなくなります。

class WeirdDictionary : IDictionary<Type, IEnumerable>
{
    private readonly Dictionary<Type, IEnumerable> _data =
        new Dictionary<Type, IEnumerable>();

    public void Add<T>(IEnumerable<T> value)
    {
        _data.Add(typeof(T), value);
    }

    public bool TryGet<T>(out IEnumerable<T> value)
    {
        IEnumerable enumerable;
        if (_data.TryGetValue(typeof(T), out enumerable)
        {
            value = (IEnumerable<T>)enumerable;
            return true;
        }

        value = null;
        return false;
    }

    // use explicit implementation to discourage use of this method since
    // the manual type checking is much slower that the generic version above
    void IDictionary<Type, IEnumerable>.Add(Type key, IEnumerable value)
    {
        if (key == null)
            throw new ArgumentNullException("key");
        if (value != null && !typeof(IEnumerable<>).MakeGenericType(key).IsAssignableFrom(value.GetType()))
            throw new ArgumentException(string.Format("'value' does not implement IEnumerable<{0}>", key));

        _data.Add(key, value);
    }
}

終了280Z28

7
Rubys

カスタムディクショナリクラスを作成します。

public class BaseClassDictionary<T, IEnumerable<T>> : Dictionary<T, IEnumerable<T>>
    where T : BaseClass
{
}

次に、この特殊な辞書をフィールドタイプとして代わりに使用できます。

private BaseClassDictionary<BaseClassDerivedType, IEnumerable<BaseClassDerivedType>> myDictionary;
4

これを試して:

public class MyCustomDictionary<T>: Dictionary<T, IEnumerable<T>>  { } 
1
Charles Bretana