web-dev-qa-db-fra.com

Dictionnaire .NET: obtenez ou créez de nouveaux

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:

39
Rok Strniša

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.

47
Adam Houldsworth

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);
}
4
Servy

Et qu'en est-il?

var keyValues = dictionary[key] = dictionary.ContainsKey(key) ? dictionary[key] : new List<string>();
keyValues.Add(aValueForKey);
0
George

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"];
0
Alex

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.

0
Francesco De Lisi