none
Behaviour of Dictionary<>

    Question

  • While the Add() method of a HashSet doesn't moan when a value exist already, a dictionary throws an exception "duplicate key". In many cases developer use code like this:

    if (!myDict.ContainsKey("himalaya"))
    {
           myDict.Add("himalaya", 1960);
    }

    Sometimes the same behaviour like that of a HashSet is needed: Just ignore the Add() if the key exists already.

    In other cases, developers Need to update the value in case the key exists already. Code often seen looks like this:

    if (myDict.ContainsKey("nanga parbat"))
    {
        myDict["nanga parbat"] = 1934;
    }
    else
    {
        myDict.Add("nanga parbat", 1934);
    }

    This is code useful if one wants to update the value of an already existing key/value pair or if absent, add the key/value pair.

    However the dictionary could encapsulate such code within ist class and allow developers to configure the dictionary in case of the above mentioned szenarios.

    For example, if you just want to ignore an Add in case the key exists already, the dictionary could be configured like this and does not throw any exception:

    myDict.Behaviour = Behaviour.IgnoreAddOnExistingKey;

    In case one wants to update the value if a key exists, the dictionary could be configured like this:

    myDict.Behaviour = Behaviour.UpdateValueOnExistingKey;

    The latter would just update the value in case the key exists when trying to add a key/value pair. Example:

    myDict.Add("himalaya", "1959");
    myDict.Behaviour = Behaviour.UpdateValueOnExistingKey;
    myDict.Add("himalaya", "1960");
    
    // second add method call just updates value for key "himalaya"

    The enum Behaviour would have the following enums:

    Behaviour.IgnoreAddOnExistingKey
    Behaviour.UpdateValueOnExistingKey
    Behaviour.Normal
    

    The enum Behaviour could also be used with an overloaded Add() method on a per-call basis.

    Like this, ContainsKey() method calls are no more neccessary for the above mentioned szenarios.

      


    Peter

    • Moved by Ivan Dragov Tuesday, May 2, 2017 7:41 AM Writtte
    Tuesday, April 25, 2017 2:22 PM

All replies

  • Peter,

    I understand this is a suggestion you would like to make to enhance the implementation of the Dictionary object. There is a site on the web that is intended exactly for that kind of suggestions, and the respective teams do keep track of it.

    Please head on over to https://visualstudio.uservoice.com/forums/121579-visual-studio-ide/category/30931-languages-c to file your suggestion.

    As a sidenote, these forums are intended to be in German. If you're after a general C# forum in English language, try https://social.msdn.microsoft.com/Forums/en-US/home?forum=csharpgeneral

    Thanks.
    Thursday, April 27, 2017 8:40 AM
  • Hi Peter,

    the different behavior for generic dictionaries introduced with .NET 2.0 is to avoid unintentionally data changes, that can lead to undetected errors.

    Instead of using an enumeration, that will result in complicated code handling, you can add the requested functionally with C# extension methods. The two cases you mentioned for example:

        public static class DictionaryExtensions
        {
            public static V AddIfNotExists<K,V>(this Dictionary<K, V> dict, K key, V value)
            {
                if (dict == null)
                    throw new ArgumentNullException("dict");
    
                V existingValue = default(V);
                if (!dict.TryGetValue(key, out existingValue))
                {
                    dict.Add(key, value);
                    return value;
                }
                return existingValue; // Optional: return existing value
            }
    
            public static void AddOrReplace<K, V>(this Dictionary<K, V> dict, K key, V value)
            {
                if (dict == null)
                    throw new ArgumentNullException("dict");
    
                V existingValue = default(V);
                if (!dict.TryGetValue(key, out existingValue))
                {
                    dict.Add(key, value);
                }
                else
                {
                    dict[key] = value;
                }
            }
        }

    Than your code can look like this:

                var myDict = new Dictionary<string, string>();
    
                myDict.Add("himalaya", "1959");
                try
                {
                    myDict.Add("himalaya", "1960");
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Add exception raises {e.Message}");
                }
    
                myDict.AddOrReplace("himalaya", "1960");
                myDict.AddIfNotExists("anden", "1961");
                myDict.AddIfNotExists("anden", "1962");
    
                foreach (var kvp in myDict)
                {
                    Console.WriteLine($"{kvp.Key} => {kvp.Value}");
                }
    
    Regards, Elmar
    • Proposed as answer by Ivan Dragov Friday, April 28, 2017 11:18 AM
    Thursday, April 27, 2017 12:05 PM
  • If you do myDict["nanga parbat"] = 1934; and the key is not yet in the dictionary, then the indexer adds it, so there is no need to check with ContainsKey first.
    Tuesday, May 2, 2017 8:21 PM