Implementation for Dictionary.TryAdd (dotnet/coreclr#9923)
authorJames Ko <jamesqko@gmail.com>
Mon, 6 Mar 2017 18:44:34 +0000 (13:44 -0500)
committerDan Moseley <danmose@microsoft.com>
Mon, 6 Mar 2017 18:44:34 +0000 (10:44 -0800)
* Add Dictionary.TryAdd

* Fix the conflicts for real

* Remove branch from Add(TKey, TValue)

* Tweak naming

* Remove a slow assert

* PR feedback

Commit migrated from https://github.com/dotnet/coreclr/commit/aeef0d20caceb33c80312fdf40ed242139cdd003

src/coreclr/src/mscorlib/src/System/Collections/Generic/Dictionary.cs

index c48c16c..a379e94 100644 (file)
@@ -50,6 +50,27 @@ namespace System.Collections.Generic
     using System.Diagnostics.Contracts;
     using System.Runtime.Serialization;
 
+    /// <summary>
+    /// Used internally to control behavior of insertion into a <see cref="Dictionary{TKey, TValue}"/>.
+    /// </summary>
+    internal enum InsertionBehavior : byte
+    {
+        /// <summary>
+        /// The default insertion behavior.
+        /// </summary>
+        None = 0,
+
+        /// <summary>
+        /// Specifies that an existing entry with the same key should be overwritten if encountered.
+        /// </summary>
+        OverwriteExisting = 1,
+
+        /// <summary>
+        /// Specifies that if an existing entry with the same key is encountered, an exception should be thrown.
+        /// </summary>
+        ThrowOnExisting = 2
+    }
+
     [DebuggerTypeProxy(typeof(Mscorlib_DictionaryDebugView<,>))]
     [DebuggerDisplay("Count = {Count}")]
     [Serializable]
@@ -241,13 +262,15 @@ namespace System.Collections.Generic
             }
             set
             {
-                Insert(key, value, false);
+                bool modified = TryInsert(key, value, InsertionBehavior.OverwriteExisting);
+                Debug.Assert(modified);
             }
         }
 
         public void Add(TKey key, TValue value)
         {
-            Insert(key, value, true);
+            bool modified = TryInsert(key, value, InsertionBehavior.ThrowOnExisting);
+            Debug.Assert(modified); // If there was an existing key and the Add failed, an exception will already have been thrown.
         }
 
         void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> keyValuePair)
@@ -402,7 +425,7 @@ namespace System.Collections.Generic
             freeList = -1;
         }
 
-        private void Insert(TKey key, TValue value, bool add)
+        private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior)
         {
             if (key == null)
             {
@@ -421,13 +444,19 @@ namespace System.Collections.Generic
             {
                 if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key))
                 {
-                    if (add)
+                    if (behavior == InsertionBehavior.OverwriteExisting)
+                    {
+                        entries[i].value = value;
+                        version++;
+                        return true;
+                    }
+
+                    if (behavior == InsertionBehavior.ThrowOnExisting)
                     {
                         ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key);
                     }
-                    entries[i].value = value;
-                    version++;
-                    return;
+
+                    return false;
                 }
 
 #if FEATURE_RANDOMIZED_STRING_HASHING
@@ -472,6 +501,7 @@ namespace System.Collections.Generic
             }
 #endif
 
+            return true;
         }
 
         public virtual void OnDeserialization(Object sender)
@@ -513,7 +543,7 @@ namespace System.Collections.Generic
                     {
                         ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_NullKey);
                     }
-                    Insert(array[i].Key, array[i].Value, true);
+                    Add(array[i].Key, array[i].Value);
                 }
             }
             else
@@ -625,6 +655,8 @@ namespace System.Collections.Generic
             return defaultValue;
         }
 
+        public bool TryAdd(TKey key, TValue value) => TryInsert(key, value, InsertionBehavior.None);
+
         bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
         {
             get { return false; }