Existe-t-il une manière plus élégante d'ajouter un élément à un dictionnaire <> en toute sécurité?

141

J'ai besoin d'ajouter des paires clé / objet à un dictionnaire, mais je dois bien sûr d'abord vérifier si la clé existe déjà, sinon j'obtiens une erreur " clé existe déjà dans le dictionnaire ". Le code ci-dessous résout cela mais est maladroit.

Quelle est la manière la plus élégante de faire cela sans créer une méthode d'assistance de chaîne comme celle-ci?

using System;
using System.Collections.Generic;

namespace TestDictStringObject
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<string, object> currentViews = new Dictionary<string, object>();

            StringHelpers.SafeDictionaryAdd(currentViews, "Customers", "view1");
            StringHelpers.SafeDictionaryAdd(currentViews, "Customers", "view2");
            StringHelpers.SafeDictionaryAdd(currentViews, "Employees", "view1");
            StringHelpers.SafeDictionaryAdd(currentViews, "Reports", "view1");

            foreach (KeyValuePair<string, object> pair in currentViews)
            {
                Console.WriteLine("{0} {1}", pair.Key, pair.Value);
            }
            Console.ReadLine();
        }
    }

    public static class StringHelpers
    {
        public static void SafeDictionaryAdd(Dictionary<string, object> dict, string key, object view)
        {
            if (!dict.ContainsKey(key))
            {
                dict.Add(key, view);
            }
            else
            {
                dict[key] = view;
            }
        }
    }
}
Edward Tanguay
la source

Réponses:

246

Utilisez simplement l'indexeur - il écrasera s'il est déjà là, mais il n'est pas nécessaire qu'il y soit d'abord:

Dictionary<string, object> currentViews = new Dictionary<string, object>();
currentViews["Customers"] = "view1";
currentViews["Customers"] = "view2";
currentViews["Employees"] = "view1";
currentViews["Reports"] = "view1";

En gros, utilisez Addsi l'existence de la clé indique un bogue (donc vous voulez qu'elle soit levée) et l'indexeur sinon. (C'est un peu comme la différence entre la diffusion et l'utilisation aspour les conversions de référence.)

Si vous utilisez C # 3 et que vous avez un ensemble distinct de clés , vous pouvez rendre cela encore plus net:

var currentViews = new Dictionary<string, object>()
{
    { "Customers", "view2" },
    { "Employees", "view1" },
    { "Reports", "view1" },
};

Cela ne fonctionnera pas dans votre cas, car les initialiseurs de collection utilisent toujours Addce qui lancera la deuxième Customersentrée.

Jon Skeet
la source
6
Excellent, je n'ai pas réalisé qu'une simple affectation a pris en charge le problème d'ajout / d'écrasement, bien.
Edward Tanguay
49

Quel est le problème avec...

dict[key] = view;

Il ajoutera automatiquement la clé si elle est inexistante.

Mehrdad Afshari
la source
3
Une chose que je pense vaut la peine de noter que si vous stockez un int, dict[key] += amountne fonctionnera pas si la clé n'existe pas
Chris S
22

simplement

dict[key] = view;

À partir de la documentation MSDN de Dictionary.Item

La valeur associée à la clé spécifiée. Si la clé spécifiée n'est pas trouvée, une opération get lève une KeyNotFoundException et une opération set crée un nouvel élément avec la clé spécifiée .

Mon accent

Steve Gilham
la source
10

Comme d'habitude, John Skeet arrive avec une vitesse d'éclairage avec la bonne réponse, mais il est intéressant de noter que vous auriez également pu écrire votre SafeAdd en tant que méthode d'extension sur IDictionary.

public static void SafeAdd(this IDictionary<K, T>. dict, K key, T value)...
rohancragg
la source
9

Bien que l'utilisation de l'indexeur soit clairement la bonne réponse à votre problème spécifique, une autre réponse plus générale au problème de l'ajout de fonctionnalités supplémentaires à un type existant serait de définir une méthode d'extension.

Évidemment, ce n'est pas un exemple particulièrement utile, mais quelque chose à garder à l'esprit pour la prochaine fois que vous trouverez un réel besoin:

public static class DictionaryExtensions
{
    public static void SafeAdd<TKey, TValue>(this Dictionary<TKey, TValue> dict, 
                                             TKey key, TValue value)
    {
        dict[key] = value;
    }
}
Daniel Earwicker
la source
2
Je mentionnerais que cela ne s'applique qu'à C # 3.0 et au-dessus.
Mehrdad Afshari