You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Propose Adding a GetOrAdd(TKey key, Func<TKey, TValue> valueFactory) to Dictionary<TKey, TValue> #15059
On ConcurrentDictionary<TKey, TValue> there is the very useful function TValue GetOrAdd(TKey key, Func<TKey, TValie> valueFactory). The pattern that the method represents is one that issued commonly on the "normal" Dictionary<TKey, TValue> as well. I propose adding such a method for this common use case. Besides simplifying it will also make switching between the two dictionaries simpler.
That's what I get for thinking of an idea, never getting around to suggesting it. And then remembering months later and forgetting to check again if it had been suggested. Thanks!
I assumed this meant Dictionary.GetOrAdd was added, but I'm not finding it or an IDictionary extension method. If they have been skipped, can this issue be reopened?
This needs API review. I don't want to rush API reviews. That is just begging for troubles later - you miss things and then you live with your mistakes forever.
It is convenience API, we lived without it for quite some time, we can IMO survive one more release. It is not the end of the world after all and not the last .NET Core release. It does not block scenarios.
Probably not the answer you expected, but this is how I feel about it. We should IMO focus primarily on the really impactful things. Makes sense?
Thanks for understanding!
IMO it is not that much about decisions, but us being open about prioritization. To ship high-quality SW, we gotta prioritize. And we gotta stabilize.
@jnm2 I don't think it would be a good idea to add GetOrAdd as an extension method on IDictionary-- it would give the impression that only 1 lookup is being made. I think it's best just to make this proposal about Dictionary<K, V>.
@karelz This is not necessarily a convenience API. For Dictionary, this helps avoid duplicate hashtable lookups, and the last time I checked I think I saw around 50 non-test instances of
Then a "compute the hash code once" code sample would look like:
refTValuerefToMyValue=CollectionsMarshal.GetValueOrAddDefault(dict,"myKey",outboolexists);if(!exists){// the dictionary just performed the equivalent of Add("myKey", default(TValue)) - let's overwrite it nowrefToMyValue=CreateValue();}
As Jan said, this would avoid the generic explosion problem, and it moves the micro-perf API to its own type. The downside is that CollectionsMarshal is by its nature somewhat unsafe. Not in the "violates type safety" sense, but more in the "you're juggling knives and could corrupt the dictionary's internal state" sense.
As mentioned, one of the problems with providing the factory is "is the key by itself sufficient to create the value, or does creation of the value rely on more information than just the key?" If the latter (which I believe is common), you really need to provide a state object.
@FiniteReality We need to be careful about adding generic instance methods to generic types. There's a real risk that it could cause significant footprint or memory bloat, even for people who don't use the new feature. Generally we want new features to be pay-for-play and to incur no overhead if you don't use them.
@GrabYourPitchforks Understandable, but doesn't that somewhat fly in the face of being able to supply a state object? From what I can tell, the API @alrz suggested would have had the same issues, but would have been even more awkward to interact with.
Agreed that a CollectionsMarshall approach is the preferred solution here. It would let users author their own variants of GetOrAdd as extension methods, without having to do double lookups.
Activity
JonHanna commentedon Aug 18, 2015
Suggested at #14676 within the comment thread too. Possible implementation at https://github.com/hackcraft/coreclr/commit/1a982053ae39ee0e418d370fdee3497ecefd0e5a
shmuelie commentedon Aug 18, 2015
That's what I get for thinking of an idea, never getting around to suggesting it. And then remembering months later and forgetting to check again if it had been suggested. Thanks!
JonHanna commentedon Aug 18, 2015
Never mind, I do check for duplicates and I still end up asking something that's been asked before, or submitting a PR that does the same as another.
jnm2 commentedon May 12, 2017
I assumed this meant Dictionary.GetOrAdd was added, but I'm not finding it or an IDictionary extension method. If they have been skipped, can this issue be reopened?
svick commentedon May 12, 2017
@jnm2 As far as I can tell, @SamuelEnglard closed this issue in favor of https://github.com/dotnet/corefx/issues/1942, which was about
TryAdd, but also discussedGetOrAdd. But then https://github.com/dotnet/corefx/issues/1942 was closed afterTryAddwas added.So yes, I think it makes sense to repoen this issue.
jnm2 commentedon May 12, 2017
Ugh, I can't believe this missed .NET Standard 2.0. @karelz no exceptions?
jnm2 commentedon May 12, 2017
Specifically:
karelz commentedon May 12, 2017
This needs API review. I don't want to rush API reviews. That is just begging for troubles later - you miss things and then you live with your mistakes forever.
It is convenience API, we lived without it for quite some time, we can IMO survive one more release. It is not the end of the world after all and not the last .NET Core release. It does not block scenarios.
Probably not the answer you expected, but this is how I feel about it. We should IMO focus primarily on the really impactful things. Makes sense?
jnm2 commentedon May 12, 2017
It is indeed the answer I expected. 😄
jnm2 commentedon May 12, 2017
(Don't worry about me, I'm not the type that goes around second-guessing the team's decisions. Fyi.)
karelz commentedon May 12, 2017
Thanks for understanding!
IMO it is not that much about decisions, but us being open about prioritization. To ship high-quality SW, we gotta prioritize. And we gotta stabilize.
jamesqo commentedon Aug 1, 2017
@jnm2 I don't think it would be a good idea to add
GetOrAddas an extension method onIDictionary-- it would give the impression that only 1 lookup is being made. I think it's best just to make this proposal aboutDictionary<K, V>.@karelz This is not necessarily a convenience API. For
Dictionary, this helps avoid duplicate hashtable lookups, and the last time I checked I think I saw around 50 non-test instances ofin corefx.
jnm2 commentedon Aug 1, 2017
@jamesqo Yeah, okay. After the BCL has it for a while and we get default interface methods it might make more sense.
19 remaining items
GrabYourPitchforks commentedon Oct 31, 2020
CollectionsMarshalwould definitely be more powerful. For example, if we modified the linked proposal to instead read:Then a "compute the hash code once" code sample would look like:
As Jan said, this would avoid the generic explosion problem, and it moves the micro-perf API to its own type. The downside is that
CollectionsMarshalis by its nature somewhat unsafe. Not in the "violates type safety" sense, but more in the "you're juggling knives and could corrupt the dictionary's internal state" sense.svick commentedon Oct 31, 2020
@jkotas
Keep in mind it's not really about simplifying from two lines of code to one, it's about changing this code:
to this code:
The latter seems much easier to understand to me.
GrabYourPitchforks commentedon Oct 31, 2020
As mentioned, one of the problems with providing the factory is "is the key by itself sufficient to create the value, or does creation of the value rely on more information than just the key?" If the latter (which I believe is common), you really need to provide a state object.
alrz commentedon Oct 31, 2020
And that easily gets convoluted:
I'd personally prefer the first one.
FiniteReality commentedon Oct 31, 2020
Wouldn't passing the state as a second parameter be way better, like ConcurrentDictionary?
GrabYourPitchforks commentedon Oct 31, 2020
@FiniteReality We need to be careful about adding generic instance methods to generic types. There's a real risk that it could cause significant footprint or memory bloat, even for people who don't use the new feature. Generally we want new features to be pay-for-play and to incur no overhead if you don't use them.
FiniteReality commentedon Oct 31, 2020
@GrabYourPitchforks Understandable, but doesn't that somewhat fly in the face of being able to supply a state object? From what I can tell, the API @alrz suggested would have had the same issues, but would have been even more awkward to interact with.
GrabYourPitchforks commentedon Oct 31, 2020
Yes, it does fly in the face of being able to provide a state object, unless the parameter is typed as object? or some similar non-generic type.
FiniteReality commentedon Oct 31, 2020
Oh joy! That's one of the things I hate most about using System.Threading.Timer 😅
alrz commentedon Nov 1, 2020
I don't understand this claim. Could you elaborate?
eiriktsarpalis commentedon Nov 2, 2020
Agreed that a
CollectionsMarshallapproach is the preferred solution here. It would let users author their own variants ofGetOrAddas extension methods, without having to do double lookups.layomia commentedon Feb 11, 2021
I'm closing this issue in favor of #27062 which offers a more capable solution. See that issue for more details.
eiriktsarpalis commentedon Oct 29, 2021
FWIW here's how one can author the originally proposed method as an extension method in .NET 6: