Existe uma maneira mais elegante de adicionar um item a um Dicionário <> com segurança?

141

Preciso adicionar pares de chave / objeto a um dicionário, mas é claro que preciso primeiro verificar se a chave já existe, caso contrário, recebo o erro " chave já existe no dicionário ". O código abaixo resolve isso, mas é desajeitado.

Qual é a maneira mais elegante de fazer isso sem criar um método auxiliar de string como este?

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
fonte

Respostas:

246

Basta usar o indexador - ele sobrescreverá se já estiver lá, mas não precisa estar lá primeiro:

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

Basicamente, use Addse a existência da chave indicar um bug (então você deseja que ela seja lançada) e o indexador, caso contrário. É um pouco como a diferença entre transmitir e usar aspara conversões de referência.

Se você estiver usando C # 3 e tiver um conjunto distinto de chaves , poderá tornar isso ainda mais limpo:

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

Isso não funcionará no seu caso, pois os inicializadores de coleção sempre usam o Addque será lançado na segunda Customersentrada.

Jon Skeet
fonte
6
Excelente, não percebeu que a tarefa simples cuidava da questão de adição / substituição, legal.
237 Edward Tanguay
49

O que há de errado com...

dict[key] = view;

Ele adicionará automaticamente a chave se ela não existir.

Mehrdad Afshari
fonte
3
Uma coisa que eu acho que é importante notar que se você estiver armazenando um int, dict[key] += amountnão vai funcionar se a chave não existe
Chris S
22

simplesmente

dict[key] = view;

Na documentação do Dictionary.Item da MSDN

O valor associado à chave especificada. Se a chave especificada não for encontrada, uma operação get lançará um KeyNotFoundException e uma operação set criará um novo elemento com a chave especificada .

Minha ênfase

Steve Gilham
fonte
10

Como sempre, John Skeet chega lá com velocidade de iluminação com a resposta certa, mas, curiosamente, você também pode ter escrito seu SafeAdd como um método de extensão no IDictionary.

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

Embora o uso do indexador seja claramente a resposta certa para o seu problema específico, outra resposta mais geral ao problema de adicionar funcionalidade adicional a um tipo existente seria definir um método de extensão.

Obviamente, este não é um exemplo particularmente útil, mas algo a ser lembrado na próxima vez que você encontrar uma necessidade real:

public static class DictionaryExtensions
{
    public static void SafeAdd<TKey, TValue>(this Dictionary<TKey, TValue> dict, 
                                             TKey key, TValue value)
    {
        dict[key] = value;
    }
}
Daniel Earwicker
fonte
2
Eu mencionaria que isso é aplicável apenas ao C # 3.0 e superior.
Mehrdad Afshari