Czy istnieje bardziej elegancki sposób bezpiecznego dodawania pozycji do słownika <>?

141

Muszę dodać pary klucz / obiekt do słownika, ale oczywiście najpierw muszę sprawdzić, czy klucz już istnieje, w przeciwnym razie pojawia się błąd „ klucz już istnieje w słowniku ”. Poniższy kod rozwiązuje ten problem, ale jest niezgrabny.

Jaki jest bardziej elegancki sposób zrobienia tego bez tworzenia takiej metody pomocniczej dla stringów?

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
źródło

Odpowiedzi:

246

Po prostu użyj indeksatora - nadpisze, jeśli już tam jest, ale nie musi tam być najpierw:

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

Zasadniczo użyj, Addjeśli istnienie klucza wskazuje na błąd (więc chcesz, aby zgłosił), a indeksator w przeciwnym razie. (To trochę jak różnica między rzutowaniem a używaniem asdo konwersji referencyjnych).

Jeśli używasz języka C # 3 i masz odrębny zestaw kluczy , możesz to zrobić jeszcze lepiej:

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

To jednak nie zadziała w twoim przypadku, ponieważ inicjatory kolekcji zawsze używają, Addco spowoduje wyświetlenie drugiego Customerswpisu.

Jon Skeet
źródło
6
Świetnie, nie zdawałem sobie sprawy, że proste zadanie rozwiązało problem z dodawaniem / nadpisywaniem, fajnie.
Edward Tanguay
49

Co jest źle z...

dict[key] = view;

Automatycznie doda klucz, jeśli nie istnieje.

Mehrdad Afshari
źródło
3
Myślę, że warto zauważyć, że jeśli przechowujesz int, dict[key] += amountnie zadziała, jeśli klucz nie istnieje
Chris S
22

po prostu

dict[key] = view;

Z dokumentacji MSDN Dictionary.Item

Wartość skojarzona z określonym kluczem. Jeśli określony klucz nie zostanie znaleziony, operacja get zgłasza KeyNotFoundException, a operacja set tworzy nowy element z określonym kluczem .

Mój nacisk

Steve Gilham
źródło
10

Jak zwykle John Skeet dostaje się tam z szybkością oświetlenia z właściwą odpowiedzią, ale co ciekawe, możesz również napisać SafeAdd jako metodę rozszerzenia w IDictionary.

public static void SafeAdd(this IDictionary<K, T>. dict, K key, T value)...
rohancragg
źródło
9

Chociaż użycie indeksatora jest z pewnością właściwą odpowiedzią na konkretny problem, inną bardziej ogólną odpowiedzią na problem związany z dodaniem dodatkowej funkcjonalności do istniejącego typu byłoby zdefiniowanie metody rozszerzenia.

Oczywiście nie jest to szczególnie przydatny przykład, ale coś, o czym należy pamiętać, gdy następnym razem znajdziesz prawdziwą potrzebę:

public static class DictionaryExtensions
{
    public static void SafeAdd<TKey, TValue>(this Dictionary<TKey, TValue> dict, 
                                             TKey key, TValue value)
    {
        dict[key] = value;
    }
}
Daniel Earwicker
źródło
2
Wspomniałbym, że dotyczy to tylko języka C # 3.0 i nowszych.
Mehrdad Afshari