ChatGPT解决这个技术问题 Extra ChatGPT

How can I detect if this dictionary key exists in C#?

I am working with the Exchange Web Services Managed API, with contact data. I have the following code, which is functional, but not 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...
}

As I said, this code works. Now I want to make it suck a little less, if possible.

I can't find any methods that allow me to check for the existence of the key in the dictionary before attempting to access it, and if I try to read it (with .ToString()) and it doesn't exist then an exception is thrown:

500 The given key was not present in the dictionary.

How can I refactor this code to suck less (while still being functional)?


A
Adam Tuttle

You can use ContainsKey:

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

or TryGetValue:

dict.TryGetValue(key, out value);

Update: according to a comment the actual class here is not an IDictionary but a PhysicalAddressDictionary, so the methods are Contains and TryGetValue but they work in the same way.

Example usage:

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

Update 2: here is the working code (compiled by question asker)

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

...with the inner conditional repeated as necessary for each key required. The TryGetValue is only done once per PhysicalAddressKey (Home, Work, etc).


The TryGetValue approach seems like it may be the best bet, since I found this page: goo.gl/7YN6... but I'm not sure how to use it. In my code above, row is a 'DataRow` object, so I'm not sure your example code is right, though...
What am I doing wrong, here? c.PhysicalAddresses.TryGetValue(c.PhysicalAddresses[PhysicalAddressKey.Home].Street, row["HomeStreet"]);
@Adam Tuttle: The second parameter is an out parameter. I'll try to guess at code that works and update my answer but you'll have to forgive mistakes as I can't compile it here.
Good answer. For consistency on SO, the term "question asker" could be replaced by "OP" (short for Original Poster).
One liner (requires C# 7.0) row["HomeStreet"] = c.PhysicalAddresses.TryGetValue(PhysicalAddressKey.Home, out PhysicalAddressEntry entry) ? entry.Street.ToString() : null;
J
John Saunders

What is the type of c.PhysicalAddresses? If it's Dictionary<TKey,TValue>, then you can use the ContainsKey method.


Thanks, Adam, that's really (not) helpful. What's the class hierarchy? What's the base type?
J
JohanE

I use a Dictionary and because of the repetetiveness and possible missing keys, I quickly patched together a small method:

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

Calling it:

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

Gets the job done.


D
David Neale

PhysicalAddressDictionary.TryGetValue

 public bool TryGetValue (
    PhysicalAddressKey key,
    out PhysicalAddressEntry physicalAddress
     )

x
xul8tr

Here is a little something I cooked up today. Seems to work for me. Basically you override the Add method in your base namespace to do a check and then call the base's Add method in order to actually add it. Hope this works for you

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());
        }
    }
}