代码之家  ›  专栏  ›  技术社区  ›  TheSoftwareJedi jac

是否有IDictionary实现在缺少键时返回默认值而不是抛出?

  •  164
  • TheSoftwareJedi jac  · 技术社区  · 16 年前

    索引器进入 Dictionary IDictionary default(T)

    我知道 TryGetValue()

    myDict.FirstOrDefault(a => a.Key == someKeyKalue);
    

    我不认为它会,因为我认为它会迭代密钥而不是使用哈希查找。

    17 回复  |  直到 5 年前
        1
  •  167
  •   Chris F Carroll    4 年前

    事实上,这根本不会有效率。

    根据评论,in。Net Core 2+/Net Standard 2.1+/Net 5, MS added the extension method GetValueOrDefault()

    对于早期版本,您可以自己编写扩展方法:

    public static TValue GetValueOrDefault<TKey,TValue>
        (this IDictionary<TKey, TValue> dictionary, TKey key)
    {
        TValue ret;
        // Ignore return value
        dictionary.TryGetValue(key, out ret);
        return ret;
    }
    

    或者使用C#7.1:

    public static TValue GetValueOrDefault<TKey,TValue>
        (this IDictionary<TKey, TValue> dictionary, TKey key) =>
        dictionary.TryGetValue(key, out var ret) ? ret : default;
    

    它使用:

    • 一个表达式体方法(C#6)
    • 默认文字(C#7.1)
        2
  •  26
  •   Pang Ajmal PraveeN    4 年前

    如果你正在使用。NET Core 2或更高版本(C#7.x) CollectionExtensions 类被引入,您可以使用 GetValueOrDefault 如果字典中没有键,则获取默认值的方法。

    Dictionary<string, string> colorData = new Dictionary<string, string>();
    string color = colorData.GetValueOrDefault("colorId", string.Empty);
    
        3
  •  19
  •   nawfal Donny V.    12 年前

    携带这些扩展方法可以提供帮助。。

    public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key)
    {
        return dict.GetValueOrDefault(key, default(V));
    }
    
    public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key, V defVal)
    {
        return dict.GetValueOrDefault(key, () => defVal);
    }
    
    public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key, Func<V> defValSelector)
    {
        V value;
        return dict.TryGetValue(key, out value) ? value : defValSelector();
    }
    
        4
  •  4
  •   jpaugh JotaBe    7 年前

    CollectionExtensions.GetValueOrDefault 方法。这与公认答案中提供的实现相同。

    public static TValue GetValueOrDefault<TKey,TValue> (
       this System.Collections.Generic.IReadOnlyDictionary<TKey,TValue> dictionary,
       TKey key);
    
        5
  •  3
  •   theMayer    6 年前

    Collections.Specialized.StringDictionary

    注意事项

    它只适用于它的特殊用途,而且——在泛型之前设计——如果你需要查看整个集合,它没有一个很好的枚举器。

        6
  •  2
  •   Brian    8 年前

    现代答案

    截至。NET Core 2.0,有一个内置的扩展方法,有2个重载:

    TValue GetValueOrDefault<TKey,TValue>(TKey)
    TValue GetValueOrDefault<TKey,TValue>(TKey, TValue)
    

    用途:

    var dict = new Dictionary<string, int>();
    
    dict.GetValueOrDefault("foo");     // 0: the datatype's default
    dict.GetValueOrDefault("foo", 2);  // 2: the specified default
    

    null 当然,对于可以为null的类型。

    documentation

        7
  •  1
  •   Rory O'Kane Erce    12 年前
    public class DefaultIndexerDictionary<TKey, TValue> : IDictionary<TKey, TValue>
    {
        private IDictionary<TKey, TValue> _dict = new Dictionary<TKey, TValue>();
    
        public TValue this[TKey key]
        {
            get
            {
                TValue val;
                if (!TryGetValue(key, out val))
                    return default(TValue);
                return val;
            }
    
            set { _dict[key] = value; }
        }
    
        public ICollection<TKey> Keys => _dict.Keys;
    
        public ICollection<TValue> Values => _dict.Values;
    
        public int Count => _dict.Count;
    
        public bool IsReadOnly => _dict.IsReadOnly;
    
        public void Add(TKey key, TValue value)
        {
            _dict.Add(key, value);
        }
    
        public void Add(KeyValuePair<TKey, TValue> item)
        {
            _dict.Add(item);
        }
    
        public void Clear()
        {
            _dict.Clear();
        }
    
        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            return _dict.Contains(item);
        }
    
        public bool ContainsKey(TKey key)
        {
            return _dict.ContainsKey(key);
        }
    
        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            _dict.CopyTo(array, arrayIndex);
        }
    
        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return _dict.GetEnumerator();
        }
    
        public bool Remove(TKey key)
        {
            return _dict.Remove(key);
        }
    
        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            return _dict.Remove(item);
        }
    
        public bool TryGetValue(TKey key, out TValue value)
        {
            return _dict.TryGetValue(key, out value);
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return _dict.GetEnumerator();
        }
    }
    
        8
  •  1
  •   Jone Polvora    11 年前

    可以为字典的键查找功能定义一个接口。我可能会把它定义为:

    Interface IKeyLookup(Of Out TValue)
      Function Contains(Key As Object)
      Function GetValueIfExists(Key As Object) As TValue
      Function GetValueIfExists(Key As Object, ByRef Succeeded As Boolean) As TValue
    End Interface
    
    Interface IKeyLookup(Of In TKey, Out TValue)
      Inherits IKeyLookup(Of Out TValue)
      Function Contains(Key As TKey)
      Function GetValue(Key As TKey) As TValue
      Function GetValueIfExists(Key As TKey) As TValue
      Function GetValueIfExists(Key As TKey, ByRef Succeeded As Boolean) As TValue
    End Interface
    

    具有非泛型键的版本将允许使用非结构键类型的代码允许任意键变化,而泛型类型参数则无法实现。不应该允许使用可变 Dictionary(Of Cat, String) 作为一个可变 Dictionary(Of Animal, String) 因为后者将允许 SomeDictionaryOfCat.Add(FionaTheFish, "Fiona") .但是使用可变变量并没有什么错 字典(猫,字符串) 字典(动物,字符串) ,因为 SomeDictionaryOfCat.Contains(FionaTheFish) 应该被认为是一个格式完全正确的表达式(它应该返回 false ,不必查字典,查找任何不符合类型的东西 Cat ).

    不幸的是,真正使用这种接口的唯一方法是包装一个 Dictionary 实现接口的类中的对象。然而,根据您正在做的事情,这样的界面及其允许的差异可能会使其值得付出努力。

        9
  •  1
  •   neural    7 年前

    如果你正在使用ASP。NET MVC,您可以利用RouteValueDictionary类来完成这项工作。

    public object this[string key]
    {
      get
      {
        object obj;
        this.TryGetValue(key, out obj);
        return obj;
      }
      set
      {
        this._dictionary[key] = value;
      }
    }
    
        10
  •  1
  •   Antoine L    5 年前

    我使用封装创建了一个IDictionary,其行为与 STL映射 ,对于那些熟悉c++的人来说。对于那些没有:

    • 如果密钥不存在,下面SafeDictionary中的indexer get{}将返回默认值, 使用默认值将该键添加到字典中。这通常是你想要的行为,因为你正在查找最终会出现或很有可能出现的项目。
    • 方法Add(TK键,TV-val)的行为类似于AddOrUpdate方法,如果存在,则替换存在的值,而不是抛出。我不明白为什么m$没有AddOrUpdate方法,我认为在非常常见的情况下抛出错误是一个好主意。

    TL/DR-编写SafeDictionary是为了在任何情况下都不会抛出异常,除非出现异常情况

    代码如下:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    public class SafeDictionary<TK, TD>: IDictionary<TK, TD> {
        Dictionary<TK, TD> _underlying = new Dictionary<TK, TD>();
        public ICollection<TK> Keys => _underlying.Keys;
        public ICollection<TD> Values => _underlying.Values;
        public int Count => _underlying.Count;
        public bool IsReadOnly => false;
    
        public TD this[TK index] {
            get {
                TD data;
                if (_underlying.TryGetValue(index, out data)) {
                    return data;
                }
                _underlying[index] = default(TD);
                return default(TD);
            }
            set {
                _underlying[index] = value;
            }
        }
    
        public void CopyTo(KeyValuePair<TK, TD>[] array, int arrayIndex) {
            Array.Copy(_underlying.ToArray(), 0, array, arrayIndex,
                Math.Min(array.Length - arrayIndex, _underlying.Count));
        }
    
    
        public void Add(TK key, TD value) {
            _underlying[key] = value;
        }
    
        public void Add(KeyValuePair<TK, TD> item) {
            _underlying[item.Key] = item.Value;
        }
    
        public void Clear() {
            _underlying.Clear();
        }
    
        public bool Contains(KeyValuePair<TK, TD> item) {
            return _underlying.Contains(item);
        }
    
        public bool ContainsKey(TK key) {
            return _underlying.ContainsKey(key);
        }
    
        public IEnumerator<KeyValuePair<TK, TD>> GetEnumerator() {
            return _underlying.GetEnumerator();
        }
    
        public bool Remove(TK key) {
            return _underlying.Remove(key);
        }
    
        public bool Remove(KeyValuePair<TK, TD> item) {
            return _underlying.Remove(item.Key);
        }
    
        public bool TryGetValue(TK key, out TD value) {
            return _underlying.TryGetValue(key, out value);
        }
    
        IEnumerator IEnumerable.GetEnumerator() {
            return _underlying.GetEnumerator();
        }
    }
    
        11
  •  0
  •   Byte Commander    9 年前

    这可能需要检查一下 TryGetValue 如果是,则返回默认值 false .

     Dictionary<string, int> myDic = new Dictionary<string, int>() { { "One", 1 }, { "Four", 4} };
     string myKey = "One"
     int value = myDic.TryGetValue(myKey, out value) ? value : 100;
    

    myKey = "One" => value = 1

    myKey = "two" => value = 100

    myKey = "Four" => value = 4

    Try it online

        12
  •  0
  •   hardkoded    7 年前

    自从。您可以使用.NET core 2.0:

    myDict.GetValueOrDefault(someKeyKalue)
    
        13
  •  0
  •   NetMage    7 年前

    那么这个检查是否存在密钥的换行符呢 ContainsKey

    var myValue = myDictionary.ContainsKey(myKey) ? myDictionary[myKey] : myDefaultValue;
    

    无需实现支持默认值的新Dictionary类,只需将查找语句替换为上面的短线即可。

        14
  •  0
  •   Aryan Firouzian pooja gautam    6 年前

    这个问题有助于确认 TryGetValue 播放 FirstOrDefault

    out variables 功能,如果您添加 null-conditional operator 从C#6到方程式,你的代码可以更简单,不需要额外的扩展方法。

    var dic = new Dictionary<string, MyClass>();
    dic.TryGetValue("Test", out var item);
    item?.DoSomething();
    

    dic.TryGetValue("Test", out var item)?.DoSomething();
    

    如果我们需要/想这样做,我们应该编写一个像Jon这样的扩展方法。

        15
  •  0
  •   MarredCheese Lionia Vasilev    4 年前

    以下是@JonSkeet在C#7.1世界中的一个版本,它还允许传入可选的默认值:

    public static TV GetValueOrDefault<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue = default) => dict.TryGetValue(key, out TV value) ? value : defaultValue;
    

    default(TV) :

    public static TV GetValueOrDefault<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue) => dict.TryGetValue(key, out TV value) ? value : defaultValue;
    public static TV GetValueOrDefault2<TK, TV>(this IDictionary<TK, TV> dict, TK key) {
        dict.TryGetValue(key, out TV value);
        return value;
    }
    

    不幸的是,C#(还没有?)没有逗号运算符(或C#6提出的分号运算符),所以你必须为其中一个重载提供一个实际的函数体(喘息!)。

        16
  •  -1
  •   Damian Vogel    4 年前

    总的来说,我会支持 answer Jon Skeet ,但是我更喜欢一种可以将默认值作为参数给出的实现:

    public static TValue GetValueOrDefault<TKey, TValue> (this IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
    {
        if (dictionary.ContainsKey(key))
            return dictionary[key];
        else
            return defaultValue;
    }
    
        17
  •  -2
  •   Joel Coehoorn    16 年前

    不,否则当键存在但存储了空值时,你怎么知道区别呢?这可能意义重大。

        18
  •  -2
  •   Xavier John    4 年前