如果键不存在,则字典返回默认值

我发现自己现在在代码中经常使用当前模式

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)

现在,我可以为字典类编写一个扩展方法来为我做这件事,但我认为我可能遗漏了一些已经存在的东西。所以,有没有一种方法来做到这一点,以一种更“容易在眼睛”,而不写一个扩展方法的字典?

231058 次浏览

TryGetValue已经将该类型的默认值赋给了字典,所以你可以使用:

dictionary.TryGetValue(key, out value);

然后忽略返回值。然而,真正的只返回default(TValue),而不是某个自定义默认值(更有用的是,也不是执行委托的结果)。框架中没有更强大的内置功能。我建议两种扩展方法:

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


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

(当然,你可能想把参数检查放进去:)

不,这样的东西根本不存在。扩展方法是正确的方法,你为它取的名字(GetValueOrDefault)是一个很好的选择。

我创建了一个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);
}
}
}

这个项目是一个用于dicictionary对象的简单装饰器和一个易于使用的扩展方法。

DefaultableDictionary允许在尝试访问不存在的键或枚举dictionary中的所有值时,为提供默认值的字典创建包装器。

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

博客的用法也一样。

我确实喜欢扩展方法,但这里有一个简单的类,当我需要默认值时,我经常使用它来处理字典。

我希望这只是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实现(如果缺少则抛出异常),而不是子类的实现。