{"id":56026,"date":"2012-02-04T20:14:37","date_gmt":"2012-02-04T20:14:37","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/pfxteam\/2012\/02\/04\/building-a-custom-getoradd-method-for-concurrentdictionarytkeytvalue\/"},"modified":"2012-02-04T20:14:37","modified_gmt":"2012-02-04T20:14:37","slug":"building-a-custom-getoradd-method-for-concurrentdictionarytkeytvalue","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/building-a-custom-getoradd-method-for-concurrentdictionarytkeytvalue\/","title":{"rendered":"Building a custom GetOrAdd method for ConcurrentDictionary"},"content":{"rendered":"<p>I was recently asked by a developer about getting some additional information out of ConcurrentDictionary&lt;TKey,TValue&gt;&rsquo;s GetOrAdd method.&nbsp; <\/p>\n<p>As a reminder, GetOrAdd either returns the value for a key currently in the dictionary, or if that key doesn&rsquo;t have a value, it adds a value for the key as dictated by either a TValue provided by the caller or by executing a Func&lt;TKey,TValue&gt; provided by the caller.&nbsp; It then returns the new value.&nbsp; However, it doesn&rsquo;t tell the caller which happened; all the caller knows is that it&rsquo;s handed back the value (existing or new) that was associated with the key.&nbsp; The developer wanted to know which occurred, as he needed to notify some other code that a new value had been added.<\/p>\n<p>While the built-in GetOrAdd method doesn&rsquo;t provide this, we can build our own GetOrAdd overload to provide this behavior, building it on top of the existing TryGetValue and TryAdd methods:<\/p>\n<blockquote>\n<p><font size=\"2\" face=\"Consolas\"><font color=\"#0000ff\">public static<\/font> TValue GetOrAdd&lt;TKey, TValue&gt;(        <br>&nbsp;&nbsp;&nbsp; <font color=\"#0000ff\">this<\/font> <font color=\"#4bacc6\">ConcurrentDictionary<\/font>&lt;TKey, TValue&gt; dict,         <br>&nbsp;&nbsp;&nbsp; <\/font><font size=\"2\" face=\"Consolas\">TKey key, <font color=\"#4bacc6\">Func<\/font>&lt;TKey, TValue&gt; generator,         <br>&nbsp;&nbsp;&nbsp; <font color=\"#0000ff\">out bool<\/font> added)        <br>{        <br>&nbsp;&nbsp;&nbsp; TValue value;        <br>&nbsp;&nbsp;&nbsp; <font color=\"#0000ff\">while<\/font> (<font color=\"#0000ff\">true<\/font>)        <br>&nbsp;&nbsp;&nbsp; {        <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color=\"#0000ff\">if<\/font> (dict.TryGetValue(key, <font color=\"#0000ff\">out<\/font> value))&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; added = <font color=\"#0000ff\">false<\/font>;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color=\"#0000ff\">return<\/font> value;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }        <\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value = generator(key);        <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color=\"#0000ff\">if<\/font> (dict.TryAdd(key, value))&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; added = <font color=\"#0000ff\">true<\/font>;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color=\"#0000ff\">return<\/font> value;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }        <br>&nbsp;&nbsp;&nbsp; }        <br>}<\/font><\/p>\n<\/blockquote>\n<p>The approach here is straightforward.&nbsp; We try to get the key&rsquo;s value in the dictionary.&nbsp; If we can, then we use an out parameter to indicate that a new value was not added and we return the value we retrieved.&nbsp; If we can&rsquo;t, then we try to add a new value as generated by the provided Func&lt;TKey,TValue&gt;; assuming that succeeds, we note the addition via the out parameter and we return the newly added value.&nbsp; If the addition fails (which should only happen if another thread concurrently added an item since we called TryGetValue), then we loop around and try the whole process again.<\/p>\n<p>Using the TryGetValue, TryAdd, and TryUpdate on ConcurrentDictionary&lt;TKey,TValue&gt;, it&rsquo;s possible to build many variations of this kind of behavior.<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I was recently asked by a developer about getting some additional information out of ConcurrentDictionary&lt;TKey,TValue&gt;&rsquo;s GetOrAdd method.&nbsp; As a reminder, GetOrAdd either returns the value for a key currently in the dictionary, or if that key doesn&rsquo;t have a value, it adds a value for the key as dictated by either a TValue provided by [&hellip;]<\/p>\n","protected":false},"author":360,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[7908],"tags":[7907,7925,7916,7909],"class_list":["post-56026","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pfxteam","tag-net-4","tag-net-4-5","tag-coordination-data-structures","tag-parallel-extensions"],"acf":[],"blog_post_summary":"<p>I was recently asked by a developer about getting some additional information out of ConcurrentDictionary&lt;TKey,TValue&gt;&rsquo;s GetOrAdd method.&nbsp; As a reminder, GetOrAdd either returns the value for a key currently in the dictionary, or if that key doesn&rsquo;t have a value, it adds a value for the key as dictated by either a TValue provided by [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/56026","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/360"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=56026"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/56026\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=56026"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=56026"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=56026"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}