2017-12-13 7 views
0

辞書のクラスAのいくつかのインスタンスの関数を呼び出すforループを作成しようとしています。キーの値がない場合は、それを作成して呼び出します。 キーへの最初のアクセス時に値を作成する方法があるかのように見えます。ディクショナリをカスタムデフォルト値に初期化する

私はそれが最善のプラクティスではないと思いますけれども、私は現在、このコードを使用しています:

(dictionary[i] = dictionary.ContainsKey(arr[i]) ? dictionary[i] : new A()).Push(10); 

は、C#で、このような問題のためのクリーナーがありますか?

+0

'dictionary [i] = dictionary [i]'? – JohnyL

+0

完全なコード(少なくともメソッド - 一部ではない)を表示し、ここにも望ましい出力を表示するとよいでしょう。それ以外の場合は、あなたが望むものは明確ではありません。 – Vladimir

+1

[.NETディクショナリ:get or create new](https://stackoverflow.com/questions/16192906/net-dictionary-get-or-create-new) – Rotem

答えて

1

は私がクリーナーコードは次のようになります言うと思います:

var key = arr[i]; 
var hasKey = dictionary.ContainsKey(key); 

if (!hasKey) 
    dictionary.Add(key, new A()); 

var itemToUse = dictionary[key]; 
itemToUse.Push(10); 

それは私には思えるが、あなたが何か短いを探しています。私はあなたが本当に求めているのはショートハンドメソッドです:

キーが存在する場合は指定されたキーの値を返し、そうでない場合はデフォルト値で辞書にキーを追加します。

私は上記のコードが意図について多くのことを教えてくれると思いますが、あなたが何か違うものを求めたい場合は、次の2つの解決策を考えることができます。

最初のものは、アイテムを取得するための拡張メソッドです:

public static TValue Get<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue) 
{ 
    var hasKey = dictionary.ContainsKey(key); 

    if (!hasKey) 
     dictionary.Add(key, defaultValue); 

    return dictionary[key]; 
} 

としてあなたはそれを使用します。私は考えることができる第二の溶液は、辞書の新しい誘導体である

dict.Get(arr[i], defaultValue: new A()) 
    .Push(10); 

class DefaultDictionary<TKey, TValue> : Dictionary<TKey, TValue> 
{ 
    private readonly Func<TKey, TValue> _defaultValueFactory; 

    public DefaultDictionary(TValue defaultValue) 
    { 
     _defaultValueFactory = new Func<TKey, TValue>(x => defaultValue); 
    } 

    public DefaultDictionary(Func<TValue> defaultValueFactory) 
    { 
     _defaultValueFactory = new Func<TKey, TValue>(x => defaultValueFactory()) ?? throw new ArgumentNullException(nameof(defaultValueFactory)); 
    } 

    public DefaultDictionary(Func<TKey, TValue> defaultValueFactory) 
    { 
     _defaultValueFactory = defaultValueFactory ?? throw new ArgumentNullException(nameof(defaultValueFactory)); 
    } 

    public new TValue this[TKey key] 
    { 
     get 
     { 
      var hasKey = ContainsKey(key); 

      if (!hasKey) 
      { 
       var defaultValue = _defaultValueFactory(key); 
       Add(key, defaultValue); 
      } 

      return base[key]; 
     } 
     set 
     { 
      base[key] = value; 
     } 
    } 
} 

これの使い方は次のようになります

var dictionary = new DefaultDictionary<string, A>(() => new A()); 
// ... 
dictionary[arr[i]].Push(10); 

私は何かについて警告する必要があります。このDictionaryの派生語は、インデックス演算子を隠します。また、メンバの型としてIDictionaryを使用するのが一般的です(たとえば、private IDictionary<string, A> dictionaryをメンバとして使用)ので、キャストせずにオーバーロードされたバージョンを使用することはできません。だから、DefaultDictionaryにあなたがオーバーロードされたインデクサーを使用するたびに、あなたの変数をキャストのいずれか、または同様に、この新しい辞書のためのインタフェースを持っている:

interface IDefaultDictionary<TKey, TValue> : IDictionary<TKey, TValue> 
{ 
    new TValue this[TKey key] { get; set; } 
} 

そして、あなたのメンバーを持っている、変数は、その定義型としてそれを使用します

private IDefaultDictionary<string, A> dictionary; 

しかし、これは具体的なクラスとしてDefaultDictionaryを使用する必要があります。これはトレードオフです。

+0

お返事ありがとうございました!私はあなたのコメントを読んで多くを学んだし、私はあなたのソリューションが本当に好きです。実際の世界では、辞書から派生する問題は確かにより堅牢で保守性に見えますが、私の小規模な練習では、コード拡張の方が適しているようです。 –

+0

もちろん、拡張機能は問題を解決するのに最適です。 getとsetを行うためのハッシュ指向のメカニズムがいくつかある場合は、インデックス演算子を使用する方が適切です。それ以外の場合、拡張機能は状況を解決します。環境設定: – welrocken

1

ConcurrentDictionaryは、GetOrAdd方法(およびAddOrUpdate,TryRemoveなどの他の有用な方法)を有する。普通の辞書にGetOrAddがあればそれを使うことができます...

public static TValue GetOrAdd<TKey, TValue>(
    this IDictionary<TKey, TValue> dictionary, 
    TKey key, 
    Func<TKey, TValue> valueFactory) 
{ 
    if (dictionary == null) 
     throw new ArgumentNullException(nameof(dictionary)); 
    if (key == null) 
     throw new ArgumentNullException(nameof(key)); 
    if (valueFactory == null) 
     throw new ArgumentNullException(nameof(valueFactory)); 

    if (dictionary.TryGetValue(key, out var existingValue)) 
     return existingValue; 
    var value = valueFactory(key); 
    dictionary.Add(key, value); 
    return value; 
} 

それを使用する方法:

dictionary.GetOrAdd(i,() => new A()).Push(10); 

このバージョンでは、そのように値ファクトリを使用しています

幸いにも、あなたはおそらくDictionaryExtensionsに名前を付ける必要があります静的クラスの拡張メソッドを作成することができますnew A()は、必要な場合にのみ実行されます。別のConcurrentDictionary.GetOrAdd()オーバーロードは、パラメータとして提供された値を使用します。

このような拡張メソッドを作成すると、ConcurrentDictionaryのメソッドを厳密に反映していることがわかります。

+0

返信ありがとうございました!初期化関数を 'GetOrAdd'メソッドに渡すというのは本当に好きでした。 –

関連する問題