如果缺少键,则字典中的索引器将引发异常。是否有IDictionary的实现将返回default(T)的实现?
我知道“ TryGetValue”方法,但是不能与linq一起使用。
这会有效地满足我的需求吗:
myDict.FirstOrDefault(a => a.Key == someKeyKalue);
我认为它不会像我认为的那样会迭代键,而不是使用哈希查找。
如果缺少键,则字典中的索引器将引发异常。是否有IDictionary的实现将返回default(T)的实现?
我知道“ TryGetValue”方法,但是不能与linq一起使用。
这会有效地满足我的需求吗:
myDict.FirstOrDefault(a => a.Key == someKeyKalue);
我认为它不会像我认为的那样会迭代键,而不是使用哈希查找。
Answers:
确实,那根本不会有效。
您总是可以编写扩展方法:
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;
使用:
return (dictionary.ContainsKey(key)) ? dictionary[key] : default(TValue);
System.Collections.Generic.CollectionExtensions
,就像我刚刚尝试过的那样,它已经存在。
进行这些扩展方法会有所帮助。
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();
}
Func<K, V>
。
如果有人使用.net core 2及更高版本(C#7.X),则会引入CollectionExtensions类,并且如果字典中没有键,则可以使用GetValueOrDefault方法获取默认值。
Dictionary<string, string> colorData = new Dictionary<string, string>();
string color = colorData.GetValueOrDefault("colorId", string.Empty);
如果使用的是.Net Core,则可以使用CollectionExtensions.GetValueOrDefault方法。这与接受的答案中提供的实现相同。
public static TValue GetValueOrDefault<TKey,TValue> (
this System.Collections.Generic.IReadOnlyDictionary<TKey,TValue> dictionary,
TKey key);
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();
}
}
可以为字典的键查找功能定义一个接口。我可能将其定义为:
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")
。但是将mutable Dictionary(Of Cat, String)
用作不可变对象并没有错Dictionary(Of Animal, String)
,因为SomeDictionaryOfCat.Contains(FionaTheFish)
应该将其视为格式正确的表达式(它应该返回false
,而不必搜索字典中所有非类型的东西Cat
)。
不幸的是,唯一能够实际使用这种接口的方法是,如果将Dictionary
对象包装在实现该接口的类中。但是,取决于您在做什么,这样的接口及其允许的差异可能值得您付出努力。
这个问题有助于确认TryGetValue
剧本FirstOrDefault
这里角色。
我要提到的一个有趣的C#7功能是out变量功能,如果将C#6中的空条件运算符添加到方程中,则代码可能会更加简单,而无需其他扩展方法。
var dic = new Dictionary<string, MyClass>();
dic.TryGetValue("Test", out var item);
item?.DoSomething();
这样做的缺点是您不能像这样进行内联的所有操作;
dic.TryGetValue("Test", out var item)?.DoSomething();
如果我们需要/想要这样做,我们应该编写一种像Jon的扩展方法。
这是适用于C#7.1的@JonSkeet版本,它还允许传入可选的默认值:
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建议的分号运算符),因此对于其中一个重载,您必须具有实际的函数体(gasp!)。
我使用封装来创建一个IDictionary,其行为与STL映射非常相似,以供熟悉c ++的人员使用。对于那些不是的人:
TL / DR-编写SafeDictionary的目的是,在不正常的情况下(例如计算机内存不足(或着火)),在任何情况下都不会引发异常。它通过用AddOrUpdate行为替换Add并返回默认值而不是从索引器中抛出NotFoundException来做到这一点。
这是代码:
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();
}
}
如果为,则可以检查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
不可以,因为否则,当键存在但存储了空值时,您如何知道差异?那可能很重要。