问题
There is a minor annoyance I find myself with a lot - I have a Dictionary<TKey, TValue>
that contains values that may or may not be there.
So normal behaviour would be to use the indexer, like this:
object result = myDictionary["key"];
However, if "key"
is not in the dictionary this throws a KeyNotFoundException
, so you do this instead:
object val;
if (!myDictionary.TryGetValue("key", out val))
{
val = ifNotFound;
}
Which is fine, except that I can have a load of these in a row - TryGetValue
starts to feel awful clunky.
So option 1 is an extension method:
public static TValue TryGet<TKey, TValue>(
this Dictionary<TKey, TValue> input,
TKey key,
TValue ifNotFound = default(TValue))
{
TValue val;
if (input.TryGetValue(key, out val))
{
return val;
}
return ifNotFound;
}
Which lets me do:
object result = myDictionary.TryGet("key1") ?? ifNotFound;
int i = anotherDictionary.TryGet("key2", -1);
Which is simple enough, but an additional extension method with a name similar to existing instance methods potentially adds confusion and reduces maintainability. It's also not consistent with the dictionary's indexer set - that will handle missing keys.
So option 2 is a new implementation of IDictionary<TKey, TValue>
with a implicit cast from Dictionary<TKey, TValue>
but an indexer that returns default(TValue)
instead of throwing a KeyNotFoundException
.
That let's me do:
ForgivingDictionary<string, object> dict = myDictionary;
object val = dict["key"] ?? ifNotFound;
// do stuff to val, then...
dict["key"] = val;
So now the get and set values are consistent, but value types are messier and ForgivingDictionary
involves a lot more code.
Both methods seem 'messy' - is there a better way to do this already in .Net?
Both methods make compromises that could cause confusion, but is one more obvious/clear than the other? And why?
回答1:
When naming an extension method intended to replace an existing method, I tend to add to the method name for specificity rather than shortening it:
GetValueOrDefault(...)
As for the ForgivingDictionary
, you can constrain TKey
so that it can't be a value type. However, if you must deal with value types in it, you're going to return something for a value type and the best option is to return default(TKey)
since you can't return null
.
Honestly, I'd go with the extension method.
Edit: GetValueOrDefault()
, of course, wouldn't add to the dictionary if it didn't find the key. I would just return a default value if it wasn't found, because that's how it's named. If one wanted it to insert as well, a good name would be GetValueOrInsertDefault()
.
回答2:
I'm unable to infer from your question what should be done when a key is not found. I can imagine nothing should be done in that case, but I can also imagine the opposite. Anyway, an elegant alternative for a series of these TryGetValue-statements you describe, is using one of the following extension methods. I have provided two options, depending on whether something should be done or not when the dictionary does not contain the key:
/// <summary> Iterates over all values corresponding to the specified keys,
///for which the key is found in the dictionary. </summary>
public static IEnumerable<TValue> TryGetValues<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, IEnumerable<TKey> keys)
{
TValue value;
foreach (TKey key in keys)
if (dictionary.TryGetValue(key, out value))
yield return value;
}
/// <summary> Iterates over all values corresponding to the specified keys,
///for which the key is found in the dictionary. A function can be specified to handle not finding a key. </summary>
public static IEnumerable<TValue> TryGetValues<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, IEnumerable<TKey> keys, Action<TKey> notFoundHandler)
{
TValue value;
foreach (TKey key in keys)
if (dictionary.TryGetValue(key, out value))
yield return value;
else
notFoundHandler(key);
}
Example code on how to use this is:
TKey[] keys = new TKey{...};
foreach(TValue value in dictionary.TryGetValues(keys))
{
//some action on all values here
}
回答3:
Or perhaps
public static TValue TryGet<TKey, TValue>(this Dictionary<TKey, TValue> input,
TKey key)
{
return input.ContainsKey(key) ? input[key] : *some default value*;
}
来源:https://stackoverflow.com/questions/6214975/which-mechanism-is-a-better-way-to-extend-dictionary-to-deal-with-missing-keys-a