Есть ли более элегантный способ безопасного добавления элемента в Dictionary <>?

146

Мне нужно добавить пары ключ / объект в словарь, но мне, конечно, нужно сначала проверить, существует ли уже ключ, иначе я получаю ошибку « ключ уже существует в словаре ». Код ниже решает эту проблему, но он неуклюжий.

Каков более элегантный способ сделать это без создания такого вспомогательного метода строки?

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;
            }
        }
    }
}
Эдвард Тангуай
источник

Ответы:

251

Просто используйте индексатор - он перезапишет, если он уже есть, но он не обязательно должен быть там первым:

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

В основном используется, Addесли наличие ключа указывает на ошибку (поэтому вы хотите, чтобы он бросал), и индексатор в противном случае. (Это немного похоже на разницу между приведением типов и использованием asдля ссылочных преобразований.)

Если вы используете C # 3 и у вас есть отдельный набор ключей , вы можете сделать это еще проще:

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

Однако в вашем случае это не сработает, поскольку инициализаторы коллекций всегда используют, Addкоторый выбрасывает вторую Customersзапись.

Джон Скит
источник
7
Отлично, не понял, что простое назначение решило проблему добавления / перезаписи, приятно.
Эдвард Тангуай,
50

Что случилось с...

dict[key] = view;

Он автоматически добавит ключ, если его не существует.

Мехрдад Афшари
источник
3
Я думаю, что стоит отметить одну вещь: если вы храните int, dict[key] += amountне будет работать, если ключ не существует
Крис С.
24

просто

dict[key] = view;

Из документации MSDN Dictionary.Item

Значение, связанное с указанным ключом. Если указанный ключ не найден, операция get вызывает исключение KeyNotFoundException, а операция set создает новый элемент с указанным ключом .

Мой акцент

Стив Гилхэм
источник
11

Как обычно, Джон Скит с молниеносной скоростью попадает туда с правильным ответом, но, что интересно, вы также могли написать свой SafeAdd как метод расширения в IDictionary.

public static void SafeAdd(this IDictionary<K, T>. dict, K key, T value)...
Роханкрагг
источник
10

Хотя использование индексатора, безусловно, является правильным ответом на вашу конкретную проблему, другим более общим ответом на проблему добавления дополнительных функций к существующему типу будет определение метода расширения.

Очевидно, что это не особенно полезный пример, но о чем следует помнить в следующий раз, когда вы обнаружите реальную потребность:

public static class DictionaryExtensions
{
    public static void SafeAdd<TKey, TValue>(this Dictionary<TKey, TValue> dict, 
                                             TKey key, TValue value)
    {
        dict[key] = value;
    }
}
Дэниел Эрвикер
источник
2
Я бы упомянул, что это применимо только к C # 3.0 и выше.
Mehrdad Afshari