Jak mogę wykryć, czy ten klucz słownika istnieje w języku C #?

498

Pracuję z interfejsem API Exchange Web Services Managed API z danymi kontaktowymi. Mam następujący kod, który jest funkcjonalny , ale nie idealny:

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...
}

Jak powiedziałem, ten kod działa . Teraz chcę , jeśli to możliwe, trochę mniej ssać .

Nie mogę znaleźć żadnych metod, które pozwolą mi sprawdzić istnienie klucza w słowniku przed próbą uzyskania do niego dostępu, a jeśli spróbuję go odczytać (za pomocą .ToString()) i nie istnieje, zgłoszony zostanie wyjątek:

500
Dany klucz nie był obecny w słowniku.

Jak mogę zmienić kod tego kodu, aby mniej ssał (nadal działając)?

Adam Tuttle
źródło

Odpowiedzi:

889

Możesz użyć ContainsKey:

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

lub TryGetValue:

dict.TryGetValue(key, out value);

Aktualizacja : zgodnie z komentarzem faktyczna klasa tutaj nie jest IDictionaryale PhysicalAddressDictionary, więc metodami są ContainsiTryGetValue jednak działają one w ten sam sposób.

Przykładowe użycie:

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

Aktualizacja 2: oto działający kod (skompilowany przez pytającego)

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

... z wewnętrznymi warunkami powtarzanymi w razie potrzeby dla każdego wymaganego klucza. TryGetValue jest wykonywana tylko raz na PhysicalAddressKey (dom, praca itp.).

Mark Byers
źródło
TryGetValuePodejście wydaje się, może to być najlepszym, ponieważ znalazłem tę stronę: goo.gl/7YN6 ... ale nie jestem pewien, jak go używać. W moim powyższym kodzie rowznajduje się obiekt „DataRow”, więc nie jestem pewien, czy twój przykładowy kod ma rację, ale ...
Adam Tuttle
Co robię źle tutaj? c.PhysicalAddresses.TryGetValue(c.PhysicalAddresses[PhysicalAddressKey.Home].Street, row["HomeStreet"]);
Adam Tuttle
1
@Adam Tuttle: Drugi parametr jest parametrem wyjściowym. Spróbuję zgadnąć, który kod działa i zaktualizuję moją odpowiedź, ale będziesz musiał wybaczyć błędy, ponieważ nie mogę go tutaj skompilować.
Mark Byers
Dobra odpowiedź. Aby zachować spójność z SO, termin „pytający” może zostać zastąpiony przez „OP” (skrót od Original Poster).
Lave Loos
Jeden liniowiec (wymaga C# 7.0)row["HomeStreet"] = c.PhysicalAddresses.TryGetValue(PhysicalAddressKey.Home, out PhysicalAddressEntry entry) ? entry.Street.ToString() : null;
Ivan García Topete,
12

Jaki to rodzaj c.PhysicalAddresses? Jeśli tak Dictionary<TKey,TValue>, możesz użyć tej ContainsKeymetody.

John Saunders
źródło
Dzięki, Adam, to naprawdę (nie) pomocne. Jaka jest hierarchia klas? Jaki jest typ podstawowy?
John Saunders
3

Używam słownika, a ze względu na powtarzalność i możliwe brakujące klucze szybko połączyłem małą metodę:

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

Nazywając to:

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

Wykonuje zadanie.

JohanE
źródło
1

Oto coś, co dzisiaj ugotowałem. Wydaje się dla mnie pracować. Zasadniczo zastępujesz metodę Add w swojej podstawowej przestrzeni nazw, aby wykonać sprawdzenie, a następnie wywołujesz metodę Add bazy, aby faktycznie ją dodać. Mam nadzieję, że to Ci odpowiada

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
źródło