Словарь возвращает значение по умолчанию, если ключ не существует

Я часто использую текущий шаблон в моем коде в настоящее время

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)

Теперь я мог бы написать метод расширения для класса словаря, который делает это для меня, но я подумал, что у меня может быть что-то, что уже существует. SO, есть ли способ сделать это таким образом, чтобы это было более "легко на глаза", не введя метод расширения в словарь?

Ответ 1

TryGetValue уже назначит значение по умолчанию для словаря, поэтому вы можете просто использовать:

dictionary.TryGetValue(key, out value);

и просто игнорируйте возвращаемое значение. Однако это действительно вернет default(TValue), а не какое-то настраиваемое значение по умолчанию (и, что более полезно, результат выполнения делегата). В рамки нет ничего более мощного. Я бы предложил два метода расширения:

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();
}

(Конечно, вы можете, конечно, включить проверку аргументов:)

Ответ 2

Я знаю, что это старый пост, и я предпочитаю методы расширения, но здесь простой класс, который я использую время от времени для обработки словарей, когда мне нужны значения по умолчанию.

Я бы хотел, чтобы это было частью базового класса 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 (выбрасывание исключения, если отсутствует), а не реализацию подкласса.

Ответ 3

Я создал 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);

Сообщение в блоге об использовании.

Ответ 4

Нет, ничего подобного не существует. Метод расширения - это путь, и ваше имя для него (GetValueOrDefault) - довольно хороший выбор.