¿Cómo puedo detectar si esta clave de diccionario existe en C #?

498

Estoy trabajando con la API administrada de servicios web de Exchange, con datos de contacto. Tengo el siguiente código, que es funcional , pero no ideal:

foreach (Contact c in contactList)
{
    string openItemUrl = "https://" + service.Url.Host + "/owa/" + c.WebClientReadFormQueryString;

    row = table.NewRow();
    row["FileAs"] = c.FileAs;
    row["GivenName"] = c.GivenName;
    row["Surname"] = c.Surname;
    row["CompanyName"] = c.CompanyName;
    row["Link"] = openItemUrl;

    //home address
    try { row["HomeStreet"] = c.PhysicalAddresses[PhysicalAddressKey.Home].Street.ToString(); }
    catch (Exception e) { }
    try { row["HomeCity"] = c.PhysicalAddresses[PhysicalAddressKey.Home].City.ToString(); }
    catch (Exception e) { }
    try { row["HomeState"] = c.PhysicalAddresses[PhysicalAddressKey.Home].State.ToString(); }
    catch (Exception e) { }
    try { row["HomeZip"] = c.PhysicalAddresses[PhysicalAddressKey.Home].PostalCode.ToString(); }
    catch (Exception e) { }
    try { row["HomeCountry"] = c.PhysicalAddresses[PhysicalAddressKey.Home].CountryOrRegion.ToString(); }
    catch (Exception e) { }

    //and so on for all kinds of other contact-related fields...
}

Como dije, este código funciona . Ahora quiero hacer que apesta un poco menos , si es posible.

No puedo encontrar ningún método que me permita verificar la existencia de la clave en el diccionario antes de intentar acceder a ella, y si intento leerla (con .ToString()) y no existe, se produce una excepción:

500
La clave dada no estaba presente en el diccionario.

¿Cómo puedo refactorizar este código para absorber menos (sin dejar de ser funcional)?

Adam Tuttle
fuente

Respuestas:

890

Puedes usar ContainsKey:

if (dict.ContainsKey(key)) { ... }

o TryGetValue:

dict.TryGetValue(key, out value);

Actualización : según un comentario, la clase real aquí no es un IDictionarysino un PhysicalAddressDictionary, por lo que los métodos son Containsy TryGetValuefuncionan de la misma manera.

Ejemplo de uso:

PhysicalAddressEntry entry;
PhysicalAddressKey key = c.PhysicalAddresses[PhysicalAddressKey.Home].Street;
if (c.PhysicalAddresses.TryGetValue(key, out entry))
{
    row["HomeStreet"] = entry;
}

Actualización 2: aquí está el código de trabajo (compilado por el que hace la pregunta)

PhysicalAddressEntry entry;
PhysicalAddressKey key = PhysicalAddressKey.Home;
if (c.PhysicalAddresses.TryGetValue(key, out entry))
{
    if (entry.Street != null)
    {
        row["HomeStreet"] = entry.Street.ToString();
    }
}

... con el condicional interno repetido según sea necesario para cada clave requerida. TryGetValue solo se realiza una vez por PhysicalAddressKey (Hogar, Trabajo, etc.).

Mark Byers
fuente
El TryGetValueenfoque parece ser la mejor opción , ya que encontré esta página: goo.gl/7YN6 ... pero no estoy seguro de cómo usarla. En mi código anterior, rowes un 'objeto DataRow`, así que no estoy seguro de su código de ejemplo es correcta, aunque ...
Adam Tuttle
¿Qué estoy haciendo mal aquí? c.PhysicalAddresses.TryGetValue(c.PhysicalAddresses[PhysicalAddressKey.Home].Street, row["HomeStreet"]);
Adam Tuttle
1
@ Adam Tuttle: El segundo parámetro es un parámetro de salida. Intentaré adivinar el código que funciona y actualizaré mi respuesta, pero tendrás que perdonar los errores, ya que no puedo compilarlo aquí.
Mark Byers
Buena respuesta. Para mantener la coherencia con SO, el término "preguntador" podría reemplazarse por "OP" (abreviatura de Cartel original).
Lave Loos
One liner (requiere C# 7.0)row["HomeStreet"] = c.PhysicalAddresses.TryGetValue(PhysicalAddressKey.Home, out PhysicalAddressEntry entry) ? entry.Street.ToString() : null;
Ivan García Topete
12

¿De qué tipo es c.PhysicalAddresses? Si es así Dictionary<TKey,TValue>, puedes usar el ContainsKeymétodo.

John Saunders
fuente
Gracias, Adam, eso es realmente (no) útil. ¿Cuál es la jerarquía de clases? ¿Cuál es el tipo base?
John Saunders
3

Utilizo un diccionario y debido a la repetitividad y las posibles teclas faltantes, rápidamente arreglé un pequeño método:

 private static string GetKey(IReadOnlyDictionary<string, string> dictValues, string keyValue)
 {
     return dictValues.ContainsKey(keyValue) ? dictValues[keyValue] : "";
 }

Llamándolo:

var entry = GetKey(dictList,"KeyValue1");

Hace el trabajo.

JohanE
fuente
1

Aquí hay algo que cociné hoy. Parece funcionar para mi. Básicamente, usted anula el método Agregar en su espacio de nombres base para hacer una verificación y luego llama al método Agregar de la base para agregarlo realmente. Espero que esto funcione para usted

using System;
using System.Collections.Generic;
using System.Collections;

namespace Main
{
    internal partial class Dictionary<TKey, TValue> : System.Collections.Generic.Dictionary<TKey, TValue>
    {
        internal new virtual void Add(TKey key, TValue value)
        {   
            if (!base.ContainsKey(key))
            {
                base.Add(key, value);
            }
        }
    }

    internal partial class List<T> : System.Collections.Generic.List<T>
    {
        internal new virtual void Add(T item)
        {
            if (!base.Contains(item))
            {
                base.Add(item);
            }
        }
    }

    public class Program
    {
        public static void Main()
        {
            Dictionary<int, string> dic = new Dictionary<int, string>();
            dic.Add(1,"b");
            dic.Add(1,"a");
            dic.Add(2,"c");
            dic.Add(1, "b");
            dic.Add(1, "a");
            dic.Add(2, "c");

            string val = "";
            dic.TryGetValue(1, out val);

            Console.WriteLine(val);
            Console.WriteLine(dic.Count.ToString());


            List<string> lst = new List<string>();
            lst.Add("b");
            lst.Add("a");
            lst.Add("c");
            lst.Add("b");
            lst.Add("a");
            lst.Add("c");

            Console.WriteLine(lst[2]);
            Console.WriteLine(lst.Count.ToString());
        }
    }
}
xul8tr
fuente