What's wrong
The explicit-default overloads of GetOrCreate carry a new() constraint they never use:
Extensions/DictionaryExtensions.cs ~L31 — GetOrCreate<TKey, TVal>(this IDictionary<TKey, TVal>, TKey key, TVal defaultValue) where TVal : notnull, new()
Extensions/DictionaryExtensions.cs ~L101 — GetOrCreate<TKey, TVal>(this ConcurrentDictionary<TKey, TVal>, TKey key, TVal defaultValue) where TVal : new()
Both insert the caller-supplied defaultValue and never call new TVal(). The constraint looks copied from the one-argument GetOrCreate(key) overloads, which do need it.
Failure scenario
Each of these fails to compile with CS0310 ("'string' must be a non-abstract type with a public parameterless constructor…"), verified in a probe project against the net9.0 build:
new Dictionary<int, string>().GetOrCreate(1, "one");
new ConcurrentDictionary<int, string>().GetOrCreate(1, "one");
new Dictionary<int, IList<int>>().GetOrCreate(1, new List<int>());
So the overload whose whole point is "I'll supply the value" is unusable for strings, interfaces, abstract types, and records/classes without a parameterless constructor — the cases where the one-argument form can't help either.
Suggested fix
Remove new() from both three-argument overloads (keep notnull on TKey/TVal and the existing runtime null checks). Leave new() on the one-argument overloads. Relaxing a generic constraint is source-compatible for existing callers.
Acceptance criteria
- The three calls above compile and behave as expected (insert when missing, return existing value otherwise).
- Tests added for
string and an interface-typed value on both IDictionary and ConcurrentDictionary.
What's wrong
The explicit-default overloads of
GetOrCreatecarry anew()constraint they never use:Extensions/DictionaryExtensions.cs~L31 —GetOrCreate<TKey, TVal>(this IDictionary<TKey, TVal>, TKey key, TVal defaultValue) where TVal : notnull, new()Extensions/DictionaryExtensions.cs~L101 —GetOrCreate<TKey, TVal>(this ConcurrentDictionary<TKey, TVal>, TKey key, TVal defaultValue) where TVal : new()Both insert the caller-supplied
defaultValueand never callnew TVal(). The constraint looks copied from the one-argumentGetOrCreate(key)overloads, which do need it.Failure scenario
Each of these fails to compile with CS0310 ("'string' must be a non-abstract type with a public parameterless constructor…"), verified in a probe project against the net9.0 build:
So the overload whose whole point is "I'll supply the value" is unusable for strings, interfaces, abstract types, and records/classes without a parameterless constructor — the cases where the one-argument form can't help either.
Suggested fix
Remove
new()from both three-argument overloads (keepnotnullonTKey/TValand the existing runtime null checks). Leavenew()on the one-argument overloads. Relaxing a generic constraint is source-compatible for existing callers.Acceptance criteria
stringand an interface-typed value on bothIDictionaryandConcurrentDictionary.