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

was*_*atz 216 c# collections dictionary

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

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
Run Code Online (Sandbox Code Playgroud)

或者有时候

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

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

这两种方式都让人觉得很迂回.我真正想要的是这样的

dictionary.GetValueOrDefault(key)
Run Code Online (Sandbox Code Playgroud)

现在,我可以为字典类编写一个扩展方法来为我做这个,但我想我可能会遗漏已经存在的东西.那么,有没有办法以更简单的方式做到这一点,而无需在字典中编写扩展方法?

Jon*_*eet 280

TryGetValue 将已将类型的默认值分配给字典,因此您可以使用:

dictionary.TryGetValue(key, out value);
Run Code Online (Sandbox Code Playgroud)

并忽略返回值.然而,真正刚刚返回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();
}
Run Code Online (Sandbox Code Playgroud)

(你当然可能想要进行参数检查:)

  • 谢谢你.第3个arg的默认值怎么样?TValue defaultValue =默认值(TValue) (15认同)
  • 在更高版本中,您可以使用提供 GetValueOrDefault(key, defaultValue) 的 System.Collections.Generic。 (4认同)
  • @ProfK:你可以将密钥与`null`进行比较; 当TKey是不可为空的值类型时,它将始终返回false.我个人认为我不想这样做 - 根据我的经验,空键*几乎*总是代表一个错误. (3认同)
  • 鉴于 Jon 的回答,我被困在如何实际调用他的扩展方法上(以前从未这样做过),所以我偶然发现了这篇 MS 文章:https://msdn.microsoft.com/en-us/library/bb311042.aspx。 (3认同)
  • @ProfK:对 - 那么根据你的特定情况更改代码是有意义的,但我不想一般地提出它:) (2认同)

rob*_*ity 28

我知道这是一个老帖子,我喜欢扩展方法,但这里是一个简单的类,我不时使用它来处理字典,当我需要默认值.

我希望这只是基类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; }
  }
}
Run Code Online (Sandbox Code Playgroud)

但要注意.通过子类化和使用new(因为override在本机Dictionary类型上不可用),如果一个DictionaryWithDefault对象被上传到普通Dictionary,则调用索引器将使用基本Dictionary实现(如果丢失则抛出异常)而不是子类的实现.

  • 我认为在这种情况下继承`Dictionary`是不明智的:通过使用`new`关键字,你隐藏了非虚拟的`Item`属性.如果有人要从`Dictionary`引用类型访问它,而不是`DictionaryWithDefault`,那么它将被调用`Dictionary`的`Item`属性,而不是你的. (7认同)

Joh*_*mez 23

我创建了一个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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

该项目是IDictionary对象的简单装饰器和扩展方法,使其易于使用.

DefaultableDictionary将允许在字典周围创建包装器,该字典在尝试访问不存在的键或通过IDictionary中的所有值枚举时提供默认值.

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

关于用法的博客文章.

  • 我将索引getter写为:`public TValue this [TKey key] {get {TValue value; TryGetValue(键,输出值); 回报值; }`避免异常处理. (7认同)
  • 基于异常的流量控制... yikers!这是一个严重的反模式.请更改您的索引器实现以使用`TryGetValue`而不是`try ... catch`. (6认同)
  • 如果您没有删除提供清晰度的链接,将会很有帮助. (2认同)
  • 我喜欢你使用扩展方法来创建默认字典,而不是要求使用你的构造函数(就像我一样). (2认同)

Pat*_*her 5

不,没有类似的东西存在。扩展方法是必经之路,它的名称(GetValueOrDefault)是一个不错的选择。