web-dev-qa-db-ja.com

キーが存在しない場合にデフォルト値を返す辞書

私は最近、自分のコードで現在のパターンをかなり頻繁に使用していることに気付きました

var dictionary = new Dictionary<type, IList<othertype>>();
// Add stuff to dictionary

var somethingElse = dictionary.ContainsKey(key) ? dictionary[key] : new List<othertype>();
// Do work with the somethingelse variable

または時々

var dictionary = new Dictionary<type, IList<othertype>>();
// Add stuff to dictionary

IList<othertype> somethingElse;
if(!dictionary.TryGetValue(key, out somethingElse) {
    somethingElse = new List<othertype>();
}

これらの方法はどちらもかなり回り道をします。本当に欲しいのは

dictionary.GetValueOrDefault(key)

今、私はこれを行う辞書クラスの拡張メソッドを書くことができましたが、すでに存在するものを見逃しているかもしれないと考えました。だから、辞書に拡張メソッドを書くことなく、より「目にやさしい」方法でこれを行う方法はありますか?

205
wasatz

TryGetValueは既にタイプのデフォルト値を辞書に割り当てているため、次のように使用できます。

dictionary.TryGetValue(key, out value);

そして、戻り値を無視します。ただし、実際にはwillは、カスタムのデフォルト値ではなくdefault(TValue)を返すだけです(より便利なことに、デリゲートの実行結果)。フレームワークには、これ以上強力なものは組み込まれていません。私は2つの拡張方法を提案します:

public static TValue GetValueOrDefault<TKey, TValue>
    (this IDictionary<TKey, TValue> dictionary, 
     TKey key,
     TValue defaultValue)
{
    TValue value;
    return dictionary.TryGetValue(key, out value) ? value : defaultValue;
}

public static TValue GetValueOrDefault<TKey, TValue>
    (this IDictionary<TKey, TValue> dictionary,
     TKey key,
     Func<TValue> defaultValueProvider)
{
    TValue value;
    return dictionary.TryGetValue(key, out value) ? value
         : defaultValueProvider();
}

(もちろん、引数をチェックインすることもできます:)

261
Jon Skeet

私はこれが古い投稿であることを知っており、拡張メソッドを好みますが、ここでは、デフォルト値が必要なときに辞書を処理するために時々使用する単純なクラスを示します。

これが基本のDictionaryクラスの一部にすぎないことを願っています。

public class DictionaryWithDefault<TKey, TValue> : Dictionary<TKey, TValue>
{
  TValue _default;
  public TValue DefaultValue {
    get { return _default; }
    set { _default = value; }
  }
  public DictionaryWithDefault() : base() { }
  public DictionaryWithDefault(TValue defaultValue) : base() {
    _default = defaultValue;
  }
  public new TValue this[TKey key]
  {
    get { 
      TValue t;
      return base.TryGetValue(key, out t) ? t : _default;
    }
    set { base[key] = value; }
  }
}

ただし、注意してください。 newをサブクラス化して使用することにより(overrideはネイティブDictionary型では使用できないため)、DictionaryWithDefaultオブジェクトがプレーンDictionaryにアップキャストされる場合、インデクサーの呼び出しはサブクラスの実装ではなくベースDictionary実装を使用します(欠落している場合は例外をスローします) 。

26
roberocity

DefaultableDictionary を作成して、あなたが求めていることを正確に行います!

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace DefaultableDictionary {
    public class DefaultableDictionary<TKey, TValue> : IDictionary<TKey, TValue> {
        private readonly IDictionary<TKey, TValue> dictionary;
        private readonly TValue defaultValue;

        public DefaultableDictionary(IDictionary<TKey, TValue> dictionary, TValue defaultValue) {
            this.dictionary = dictionary;
            this.defaultValue = defaultValue;
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
            return dictionary.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }

        public void Add(KeyValuePair<TKey, TValue> item) {
            dictionary.Add(item);
        }

        public void Clear() {
            dictionary.Clear();
        }

        public bool Contains(KeyValuePair<TKey, TValue> item) {
            return dictionary.Contains(item);
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
            dictionary.CopyTo(array, arrayIndex);
        }

        public bool Remove(KeyValuePair<TKey, TValue> item) {
            return dictionary.Remove(item);
        }

        public int Count {
            get { return dictionary.Count; }
        }

        public bool IsReadOnly {
            get { return dictionary.IsReadOnly; }
        }

        public bool ContainsKey(TKey key) {
            return dictionary.ContainsKey(key);
        }

        public void Add(TKey key, TValue value) {
            dictionary.Add(key, value);
        }

        public bool Remove(TKey key) {
            return dictionary.Remove(key);
        }

        public bool TryGetValue(TKey key, out TValue value) {
            if (!dictionary.TryGetValue(key, out value)) {
                value = defaultValue;
            }

            return true;
        }

        public TValue this[TKey key] {
            get
            {
                try
                {
                    return dictionary[key];
                } catch (KeyNotFoundException) {
                    return defaultValue;
                }
            }

            set { dictionary[key] = value; }
        }

        public ICollection<TKey> Keys {
            get { return dictionary.Keys; }
        }

        public ICollection<TValue> Values {
            get
            {
                var values = new List<TValue>(dictionary.Values) {
                    defaultValue
                };
                return values;
            }
        }
    }

    public static class DefaultableDictionaryExtensions {
        public static IDictionary<TKey, TValue> WithDefaultValue<TValue, TKey>(this IDictionary<TKey, TValue> dictionary, TValue defaultValue ) {
            return new DefaultableDictionary<TKey, TValue>(dictionary, defaultValue);
        }
    }
}

このプロジェクトは、IDictionaryオブジェクトのシンプルなデコレータと、使いやすくするための拡張メソッドです。

DefaultableDictionaryを使用すると、存在しないキーにアクセスしたり、IDictionaryのすべての値を列挙したりするときにデフォルト値を提供する辞書のラッパーを作成できます。

例:var dictionary = new Dictionary<string, int>().WithDefaultValue(5);

ブログ投稿 使用方法についても。

23
John Sonmez

いいえ、そのようなものは存在しません。拡張メソッドは行く方法であり、その名前(GetValueOrDefault)はかなり良い選択です。

4
Patrick Karcher