Skip to content

Add MA0242: Use a value factory with ConcurrentDictionary.GetOrAdd/AddOrUpdate when the value is expensive - #1611

Merged
meziantou merged 1 commit into
mainfrom
feature/meziantou-analyzer-1610-e9b579
Sep 23, 2026
Merged

meziantou merged 1 commit into
mainfrom
feature/meziantou-analyzer-1610-e9b579

Conversation

@meziantou

Copy link
Copy Markdown
Owner

Fixes #1610

What

New rule MA0242 (Performance, Info, enabled by default) with a code fix.

ConcurrentDictionary<TKey, TValue>.GetOrAdd(key, value) and AddOrUpdate(key, addValue, updateValueFactory) evaluate the value before the call, even when the key is already in the dictionary. The rule reports the value arguments that are not cheap to evaluate, and suggests the overloads taking a Func<TKey, TValue>.

dict.GetOrAdd("1", value);                                   // ok
dict.GetOrAdd("2", (int)floatValue);                         // ok
dict.GetOrAdd("3", (int)Math.Ceiling(Math.Pow(value, 2)));   // MA0242
lists.GetOrAdd("key", new List<int>());                      // MA0242
  • Reported: invocations, object creations, collection expressions, string concatenations, non-constant interpolated strings, indexers, await, struct creation with a user-defined constructor.
  • Not reported: constants, locals, parameters, this, fields, properties without arguments, array elements, default, typeof, sizeof, lambdas / method groups, throw expressions, non-user-defined conversions, and built-in value-type operators / ?: / ?? / ?. / tuples whose operands are themselves cheap.

Code fix

Wraps the expression in a lambda: GetOrAdd(key, obj.ToString()) → GetOrAdd(key, _ => obj.ToString()).

  • Named arguments value: / addValue: are renamed to valueFactory: / addValueFactory:.
  • If _ is already in scope, the parameter is named _1, _2, ...
  • The fix is only registered after checking that the rewritten document binds to the factory overload and does not introduce new compilation errors (e.g. await, ref structs, out var used after the call).

Reviewer notes

  • Properties without arguments are considered cheap, which is broader than the "simple variable" wording in the issue, to avoid noise on obj.Name-style arguments.
  • The fix changes when the value is evaluated (side effects are skipped when the key exists); this is documented in docs/Rules/MA0242.md.
  • Tests pass on all Roslyn versions (roslyn4.8, 4.14, 5.0, 5.6, 5.9); full roslyn5.9 suite passes (5409 tests). dotnet run --project src/DocumentationGenerator produces no further changes.

…y is not in the ConcurrentDictionary

ConcurrentDictionary.GetOrAdd(key, value) and AddOrUpdate(key, addValue, updateValueFactory)
evaluate the value even when the key already exists. Report the values that are not cheap to
evaluate and provide a code fix that wraps them in a value factory.

Fixes #1610
@meziantou
meziantou marked this pull request as ready for review September 23, 2026 16:50
@meziantou
meziantou merged commit 5d3bb98 into main Sep 23, 2026
14 checks passed
@meziantou
meziantou deleted the feature/meziantou-analyzer-1610-e9b579 branch September 23, 2026 16:50
This was referenced Sep 23, 2026
This was referenced Sep 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Analyzer for finding ConcurrentDictionary.GetOrAdd calls which evaluate the value parameter in place

1 participant