Como posso detectar se essa chave de dicionário existe em c #?

498

Estou trabalhando com a API gerenciada de serviços Web do Exchange, com dados de contato. Eu tenho o seguinte código, que é funcional , mas não 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 eu disse, esse código funciona . Agora eu quero fazê-lo chupar um pouco menos , se possível.

Não consigo encontrar nenhum método que permita verificar a existência da chave no dicionário antes de tentar acessá-la, e se eu tentar lê-la (com .ToString()) e ela não existir, uma exceção será lançada:

500
A chave fornecida não estava presente no dicionário.

Como refatorar esse código para sugar menos (enquanto ainda está funcionando)?

Adam Tuttle
fonte

Respostas:

889

Você pode usar ContainsKey:

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

ou TryGetValue:

dict.TryGetValue(key, out value);

Atualização : de acordo com um comentário, a classe real aqui não é um IDictionarymas a PhysicalAddressDictionary, portanto os métodos são Containse TryGetValuefuncionam da mesma maneira.

Exemplo de uso:

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

Atualização 2: aqui está o código de trabalho (compilado pelo questionador)

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

... com o condicional interno repetido conforme necessário para cada chave necessária. O TryGetValue é feito apenas uma vez por PhysicalAddressKey (Home, Work, etc).

Mark Byers
fonte
A TryGetValueabordagem parece ser a melhor aposta, já que encontrei esta página: goo.gl/7YN6 ... mas não tenho certeza de como usá-la. No meu código acima, rowé um 'objeto DataRow`, então eu não tenho certeza do seu código exemplo é certo, porém ...
Adam Tuttle
O que eu estou fazendo errado aqui? c.PhysicalAddresses.TryGetValue(c.PhysicalAddresses[PhysicalAddressKey.Home].Street, row["HomeStreet"]);
Adam Tuttle
1
@ Adam Tuttle: O segundo parâmetro é um parâmetro de saída. Vou tentar adivinhar o código que funciona e atualizar minha resposta, mas você terá que perdoar erros, pois não posso compilá-lo aqui.
Mark Byers
Boa resposta. Para consistência no SO, o termo "questionador" pode ser substituído por "OP" (abreviação de Pôster original).
Lave Loos
Um forro (requer C# 7.0) #row["HomeStreet"] = c.PhysicalAddresses.TryGetValue(PhysicalAddressKey.Home, out PhysicalAddressEntry entry) ? entry.Street.ToString() : null;
1879 Ivan García Topete
12

Qual é o tipo de c.PhysicalAddresses? Se for Dictionary<TKey,TValue>, então você pode usar o ContainsKeymétodo

John Saunders
fonte
Obrigado, Adam, isso é realmente (não) útil. Qual é a hierarquia de classes? Qual é o tipo de base?
John Saunders
3

Eu uso um dicionário e, devido à repetibilidade e às possíveis chaves ausentes, rapidamente remendei um pequeno método:

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

Chamando:

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

Faz o trabalho.

JohanE
fonte
1

Aqui está uma coisinha que eu preparei hoje. Parece funcionar para mim. Basicamente, você substitui o método Add no espaço de nomes base para fazer uma verificação e depois chama o método Add da base para realmente adicioná-lo. Espero que funcione para voce

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
fonte