I often find myself creating a Dictionary
with a non-trivial value class (e.g. List
), and then always writing the same code pattern when filling in data.
For example:
var dict = new Dictionary<string, List<string>>();
string key = "foo";
string aValueForKey = "bar";
That is, I want to insert "bar"
into the list that corresponds to key "foo"
, where key "foo"
might not be mapped to anything.
This is where I use the ever-repeating pattern:
List<string> keyValues;
if (!dict.TryGetValue(key, out keyValues))
dict.Add(key, keyValues = new List<string>());
keyValues.Add(aValueForKey);
Is there a more elegant way of doing this?
Related questions that don't have answers to this question:
We have a slightly different take on this, but the effect is similar:
public static TValue GetOrCreate<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key)
where TValue : new()
{
TValue val;
if (!dict.TryGetValue(key, out val))
{
val = new TValue();
dict.Add(key, val);
}
return val;
}
Called:
var dictionary = new Dictionary<string, List<int>>();
List<int> numbers = dictionary.GetOrCreate("key");
It makes use of the generic constraint for public parameterless constructors: where TValue : new()
.
To help with discovery, unless the extension method is quite specific to a narrow problem, we tend to place extension methods in the namespace of the type they are extending, in this case:
namespace System.Collections.Generic
Most of the time, the person using the type has the using
statement defined at the top, so IntelliSense would also find the extension methods for it defined in your code.
As with so many programming problems, when you find yourself doing something a lot, refactor it into a method:
public static void MyAdd<TKey, TCollection, TValue>(
this Dictionary<TKey, TCollection> dictionary, TKey key, TValue value)
where TCollection : ICollection<TValue>, new()
{
TCollection collection;
if (!dictionary.TryGetValue(key, out collection))
{
collection = new TCollection();
dictionary.Add(key, collection);
}
collection.Add(value);
}
Ok, different approach:
public static bool TryAddValue<TKey,TValue>(this System.Collections.Generic.IDictionary<TKey,List<TValue>> dictionary, TKey key, TValue value)
{
// Null check (useful or not, depending on your null checking approach)
if (value == null)
return false;
List<TValue> tempValue = default(List<TValue>);
try
{
if (!dictionary.TryGetValue(key, out tempValue))
{
dictionary.Add(key, tempValue = new List<TValue>());
}
else
{
// Double null check (useful or not, depending on your null checking approach)
if (tempValue == null)
{
dictionary[key] = (tempValue = new List<TValue>());
}
}
tempValue.Add(value);
return true;
}
catch
{
return false;
}
}
In this way you have to "try to add" your value to a generic List of (obviously generalizable to a generic collection), null checking and trying to get existing key/values in your Dictionary. Usage and example:
var x = new Dictionary<string,List<string>>();
x.TryAddValue("test", null); // return false due to null value. Doesn't add the key
x.TryAddValue("test", "ok"); // it works adding the key/value
x.TryAddValue("test", "ok again"); // it works adding the value to the existing list
Hope it helps.
And what about this?
var keyValues = dictionary[key] = dictionary.ContainsKey(key) ? dictionary[key] : new List<string>();
keyValues.Add(aValueForKey);
来源:https://stackoverflow.com/questions/16192906/net-dictionary-get-or-create-new