¿Hay alguna forma más elegante de agregar un elemento a un Diccionario <> de forma segura?

141

Necesito agregar pares clave / objeto a un diccionario, pero, por supuesto, primero debo verificar si la clave ya existe; de ​​lo contrario, aparece el error " clave ya existe en el diccionario ". El siguiente código resuelve esto, pero es torpe.

¿Cuál es una forma más elegante de hacer esto sin hacer un método auxiliar de cadena 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
fuente

Respuestas:

246

Simplemente use el indexador; se sobrescribirá si ya está allí, pero no tiene que estar allí primero:

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

Básicamente, use Addsi la existencia de la clave indica un error (por lo que desea que se lance) y el indexador de lo contrario. (Es un poco como la diferencia entre transmitir y usar aspara conversiones de referencia).

Si está utilizando C # 3 y tiene un conjunto distinto de teclas , puede hacer esto aún más ordenado:

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

Sin embargo, eso no funcionará en su caso, ya que los inicializadores de colección siempre usan Addlo que arrojará en la segunda Customersentrada.

Jon Skeet
fuente
66
Excelente, no me di cuenta de que la asignación simple se hizo cargo del problema de agregar / sobrescribir, bueno.
Edward Tanguay
49

Qué hay de malo en...

dict[key] = view;

Agregará automáticamente la clave si no existe.

Mehrdad Afshari
fuente
3
Una cosa que creo que vale la pena señalar es que si está almacenando un int, dict[key] += amountno funcionará si la clave no existe
Chris S
22

simplemente

dict[key] = view;

De la documentación de MSDN de Dictionary.Item

El valor asociado con la clave especificada. Si no se encuentra la clave especificada, una operación get arroja una KeyNotFoundException y una operación set crea un nuevo elemento con la clave especificada .

Mi énfasis

Steve Gilham
fuente
10

Como de costumbre, John Skeet entra allí con la velocidad de la luz con la respuesta correcta, pero curiosamente también podría haber escrito su SafeAdd como método de extensión en IDictionary.

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

Aunque el uso del indexador es claramente la respuesta correcta para su problema específico, otra respuesta más general al problema de agregar funcionalidad adicional a un tipo existente sería definir un método de extensión.

Obviamente, este no es un ejemplo particularmente útil, pero es algo a tener en cuenta para la próxima vez que encuentre una necesidad 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
fuente
2
Mencionaría que esto solo es aplicable a C # 3.0 y superior.
Mehrdad Afshari