C# 是否有一个 IDictionary 实现,在缺少键时,返回默认值而不是抛出?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/538729/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there an IDictionary implementation that, on missing key, returns the default value instead of throwing?
提问by TheSoftwareJedi
The indexer into Dictionary throws an exception if the key is missing. Is there an implementation of IDictionary that instead will return default(T)?
如果缺少键,则字典中的索引器会引发异常。是否有 IDictionary 的实现会返回 default(T)?
I know about the "TryGetValue" method, but that's impossible to use with linq.
我知道“TryGetValue”方法,但这不可能与 linq 一起使用。
Would this efficiently do what I need?:
这会有效地做我需要的吗?:
myDict.FirstOrDefault(a => a.Key == someKeyKalue);
I don't think it will as I think it will iterate the keys instead of using a Hash lookup.
我认为它不会,因为我认为它会迭代键而不是使用哈希查找。
采纳答案by Jon Skeet
Indeed, that won't be efficient at all.
事实上,这根本不会有效率。
You could always write an extension method:
你总是可以写一个扩展方法:
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;
}
Or with C# 7.1:
或者使用 C# 7.1:
public static TValue GetValueOrDefault<TKey,TValue>
(this IDictionary<TKey, TValue> dictionary, TKey key) =>
dictionary.TryGetValue(key, out var ret) ? ret : default;
That uses:
这使用:
- An expression-bodied method (C# 6)
- An out variable (C# 7.0)
- A default literal (C# 7.1)
- 表达式主体方法 (C# 6)
- 输出变量 (C# 7.0)
- 默认文字 (C# 7.1)
回答by Joel Coehoorn
No, because otherwise how would you know the difference when the key exists but stored a null value? That could be significant.
不,因为否则当键存在但存储空值时,你怎么知道差异?这可能很重要。
回答by supercat
One could define an interface for the key-lookup function of a dictionary. I'd probably define it as something like:
可以为字典的键查找功能定义一个接口。我可能会将它定义为:
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
The version with non-generic keys would allow code that was using code using non-structure key types to allow for arbitrary key variance, which would not be possible with a generic type parameter. One should not be allowed to use a mutable Dictionary(Of Cat, String)
as a mutable Dictionary(Of Animal, String)
since the latter would allow SomeDictionaryOfCat.Add(FionaTheFish, "Fiona")
. But there's nothing wrong with using a mutable Dictionary(Of Cat, String)
as an immutable Dictionary(Of Animal, String)
, since SomeDictionaryOfCat.Contains(FionaTheFish)
should be considered a perfectly well-formed expression (it should return false
, without having to search the dictionary, for anything that isn't of type Cat
).
具有非泛型键的版本将允许使用非结构键类型的代码的代码允许任意键变化,这对于泛型类型参数是不可能的。不应允许将 mutableDictionary(Of Cat, String)
用作 mutable,Dictionary(Of Animal, String)
因为后者将允许SomeDictionaryOfCat.Add(FionaTheFish, "Fiona")
. 但是将 mutableDictionary(Of Cat, String)
用作 immutable并没有错Dictionary(Of Animal, String)
,因为它SomeDictionaryOfCat.Contains(FionaTheFish)
应该被认为是一个完美格式的表达式(它应该返回false
,而不必搜索字典,查找任何不是 type 的东西Cat
)。
Unfortunately, the only way one will be able to actually use such an interface is if one wraps a Dictionary
object in a class which implements the interface. Depending upon what you're doing, however, such an interface and the variance it allows might make it worth the effort.
不幸的是,能够实际使用这样一个接口的唯一方法是将一个Dictionary
对象包装在一个实现该接口的类中。然而,根据您在做什么,这样的界面和它允许的变化可能值得付出努力。
回答by nawfal
Carrying these extension methods can help..
携带这些扩展方法可以帮助..
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();
}
回答by Jone Polvora
If you are using ASP.NET MVC, you could leverage the RouteValueDictionary class that do the job.
如果您使用的是 ASP.NET MVC,您可以利用 RouteValueDictionary 类来完成这项工作。
public object this[string key]
{
get
{
object obj;
this.TryGetValue(key, out obj);
return obj;
}
set
{
this._dictionary[key] = value;
}
}
回答by Mark Hurd
Collections.Specialized.StringDictionary
provides a non-exception result when looking up a missing key's value. It is also case-insensitive by default.
Collections.Specialized.StringDictionary
在查找缺失键的值时提供非异常结果。默认情况下它也不区分大小写。
Caveats
注意事项
It is only valid for its specialized uses, and — being designed before generics — it doesn't have a very good enumerator if you need to review the whole collection.
它只对它的特殊用途有效,而且——在泛型之前设计——如果你需要查看整个集合,它没有一个很好的枚举器。
回答by Byte Commander
What about this one-liner that checks whether a key is present using ContainsKey
and then returns either the normally retreived value or the default value using the conditional operator?
这个ContainsKey
使用条件运算符检查键是否存在然后返回正常检索值或默认值的单行程序怎么样?
var myValue = myDictionary.ContainsKey(myKey) ? myDictionary[myKey] : myDefaultValue;
No need to implement a new Dictionary class that supports default values, simply replace your lookup statements with the short line above.
无需实现支持默认值的新 Dictionary 类,只需将查找语句替换为上面的短行即可。
回答by Brian
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();
}
}
回答by NetMage
Here is a version of @JonSkeet's for the world of C# 7.1 that also allows for an optional default to be passed in:
这是用于 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;
It may be more efficient to have two functions to optimize the case where you want to return default(TV)
:
使用两个函数来优化您想要返回的情况可能更有效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;
}
Unfortunately C# doesn't (yet?) have a comma operator (or the C# 6 proposed semicolon operator) so you have to have an actual function body (gasp!) for one of the overloads.
不幸的是,C# 还没有(还?)有一个逗号运算符(或 C# 6 建议的分号运算符),所以你必须有一个实际的函数体(喘气!)用于重载之一。
回答by hardkoded
This question helped to confirm that the TryGetValue
plays the FirstOrDefault
role here.
这个问题有助于确认这里TryGetValue
的FirstOrDefault
作用。
One interesting C# 7 feature I would like to mention is the out variablesfeature, and if you add the null-conditional operatorfrom C# 6 to the equation your code could be much more simple with no need of extra extension methods.
我想提到的一个有趣的 C# 7 特性是输出变量特性,如果您将C# 6 中的空条件运算符添加到等式,您的代码可能会更加简单,无需额外的扩展方法。
var dic = new Dictionary<string, MyClass>();
dic.TryGetValue("Test", out var item);
item?.DoSomething();
The downside of this is that you can't do everything inline like this;
这样做的缺点是你不能像这样内联做所有事情;
dic.TryGetValue("Test", out var item)?.DoSomething();
If we'd need/want to do this we should code one extension method like Jon's.
如果我们需要/想要这样做,我们应该编写一种像 Jon 那样的扩展方法。