.NET Dictionary: получить или создать новый
Я часто обнаруживаю, что создаю Dictionary
с нетривиальным классом значений (например, List
), а затем всегда записывать один и тот же шаблон кода при заполнении данных.
Например:
var dict = new Dictionary<string, List<string>>();
string key = "foo";
string aValueForKey = "bar";
То есть, я хочу вставить "bar"
в список, соответствующий клавише "foo"
, где ключ "foo"
может не отображаться ни на что.
Здесь я использую постоянно повторяющийся шаблон:
List<string> keyValues;
if (!dict.TryGetValue(key, out keyValues))
dict.Add(key, keyValues = new List<string>());
keyValues.Add(aValueForKey);
Есть ли более элегантный способ сделать это?
Связанные вопросы, которые не имеют ответов на этот вопрос:
Ответы
Ответ 1
Мы немного изменили это, но эффект подобен:
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;
}
Вызывается:
var dictionary = new Dictionary<string, List<int>>();
List<int> numbers = dictionary.GetOrCreate("key");
Он использует общее ограничение для открытых конструкторов без параметров: where TValue : new()
.
Чтобы помочь с обнаружением, если метод расширения не является весьма специфичным для узкой проблемы, мы склонны размещать методы расширения в пространстве имен типа, который они расширяют, в этом случае:
namespace System.Collections.Generic
В большинстве случаев человек, использующий тип, имеет оператор using
, определенный сверху, поэтому IntelliSense также найдет методы расширения, определенные в вашем коде.
Ответ 2
Как и во многих проблемах с программированием, когда вы что-то делаете, реорганизуйте его в метод:
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);
}
Ответ 3
Хорошо, другой подход:
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;
}
}
Таким образом, вам нужно "попытаться добавить" ваше значение в общий список (очевидно, обобщаемый для общей коллекции), нулевую проверку и попытку получить существующий ключ/значения в вашем словаре.
Использование и пример:
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
Надеюсь, что это поможет.
Ответ 4
А как насчет этого?
var keyValues = dictionary[key] = dictionary.ContainsKey(key) ? dictionary[key] : new List<string>();
keyValues.Add(aValueForKey);