Словарь возвращает значение по умолчанию, если ключ не существует
Я часто использую текущий шаблон в моем коде в настоящее время
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) - довольно хороший выбор.