Je me retrouve souvent à créer un Dictionary
avec une classe de valeur non triviale (par exemple List
), puis à toujours écrire le même code modèle lors du remplissage des données.
Par exemple:
var dict = new Dictionary<string, List<string>>();
string key = "foo";
string aValueForKey = "bar";
Autrement dit, je veux insérer "bar"
dans la liste qui correspond à la clé "foo"
, où clé "foo"
n'est peut-être mappé à rien.
C'est là que j'utilise le motif sans cesse répété:
List<string> keyValues;
if (!dict.TryGetValue(key, out keyValues))
dict.Add(key, keyValues = new List<string>());
keyValues.Add(aValueForKey);
Existe-t-il une manière plus élégante de procéder?
Questions connexes qui n'ont pas de réponses à cette question:
Nous avons une approche légèrement différente à ce sujet, mais l'effet est similaire:
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;
}
Appelé:
var dictionary = new Dictionary<string, List<int>>();
List<int> numbers = dictionary.GetOrCreate("key");
Il utilise la contrainte générique pour les constructeurs publics sans paramètre: where TValue : new()
.
Pour aider à la découverte, à moins que la méthode d'extension ne soit assez spécifique à un problème étroit, nous avons tendance à placer les méthodes d'extension dans l'espace de noms du type qu'elles étendent, dans ce cas:
namespace System.Collections.Generic
La plupart du temps, la personne utilisant le type a l'instruction using
définie en haut, donc IntelliSense trouverait également les méthodes d'extension pour elle définies dans votre code.
Comme pour tant de problèmes de programmation, lorsque vous vous retrouvez à faire quelque chose, refactorisez-le en une méthode:
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);
}
Et qu'en est-il?
var keyValues = dictionary[key] = dictionary.ContainsKey(key) ? dictionary[key] : new List<string>();
keyValues.Add(aValueForKey);
Si vous utilisez .Net Core, vous pouvez utiliser Dictionary<>.TryAdd()
.
var dict = new Dictionary<string, string>();
dict.TryAdd("foo", "bar"); // returns bool whether it added or not feel free to ignore.
var myValue = dict["foo"];
Ok, approche différente:
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;
}
}
De cette façon, vous devez "essayer d'ajouter" votre valeur à une liste générique de (évidemment généralisable à une collection générique), vérifier la valeur nulle et essayer d'obtenir les clés/valeurs existantes dans votre dictionnaire. Utilisation et exemple:
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
J'espère que ça aide.