Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prefer Dictionary.Remove(key, out value) over Dictionary.this[key], followed by Dictionary.Remove(key) #33800

Open
Tracked by #57797
terrajobst opened this issue Mar 19, 2020 · 7 comments
Labels
api-approved API was approved in API review, it can be implemented area-System.Collections code-analyzer Marks an issue that suggests a Roslyn analyzer code-fixer Marks an issue that suggests a Roslyn code fixer enhancement Product code improvement that does NOT require public API changes/additions help wanted [up-for-grabs] Good issue for external contributors
Milestone

Comments

@terrajobst
Copy link
Member

Dictionary.ContainsKey<K, V(key), followed by Dictionary<K, V.this[key], followed by Dictionary<K, V>.Remove(key) can be combined into a single Remove call, using the overload accepting out TValue value.

Category: Performance

@terrajobst terrajobst added api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Collections code-analyzer Marks an issue that suggests a Roslyn analyzer labels Mar 19, 2020
@jeffhandley jeffhandley added this to the Future milestone Mar 20, 2020
@jeffhandley jeffhandley added the code-fixer Marks an issue that suggests a Roslyn code fixer label Mar 21, 2020
@jeffhandley
Copy link
Member

Estimates:

  • Analyzer: Medium
  • Fixer: Medium

@carlossanlop
Copy link
Member

carlossanlop commented Jan 8, 2021

This is closely related to #33797

Suggested severity: Info

// Before
        public void RemoveValue(Dictionary<string, int> data, string key)
        {​​
            if (data.ContainsKey(key))
            {
                // Dictionary.ContainsKey<K, V(key),
                // followed by Dictionary<K, V.this[key],
                // followed by Dictionary<K, V>.Remove(key) can be combined into a single Remove call,
                // using the overload accepting out TValue value.
                Foo(data[key]);
                data.Remove(key);
            }
        }​​

// After
        public void RemoveValue(Dictionary<string, int> data, string key)
        {​​
            if (data.Remove(key, out int value))
            {
                Foo(value);
            }
        }​​

Applying the below example in the analyzer might be more complicated:

// Before
        public void RemoveValue(Dictionary<string, int> data, string key)
        {​​
            if (data.ContainsKey(key))
            {
                int x = data[key];
                data.Remove(key);
                Foo(x);
            }
        }​​

// After
        public void RemoveValue(Dictionary<string, int> data, string key)
        {​​
            if (data.Remove(key, out int value))
            {
                Foo(value);
            }
        }​​

We should also consider the case where the data is used multiple times, and when there's unrelated code in the way:

// Before
        public void RemoveValue(Dictionary<string, int> data, string key)
        {​​
            if (data.ContainsKey(key))
            {
                Foo(data[key]);
                UnrelatedMethodCall();
                Bar(data[key]);
                data.Remove(key);
            }
        }​​
// After
        public void RemoveValue(Dictionary<string, int> data, string key)
        {​​
            if (data.Remove(key, out int value))
            {
                Foo(value);
                UnrelatedMethodCall();
                Bar(value);
            }
        }​​

@carlossanlop carlossanlop added the needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration label Jan 8, 2021
@buyaa-n buyaa-n added api-ready-for-review API is ready for review, it is NOT ready for implementation and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration labels Feb 23, 2021
@buyaa-n

This comment has been minimized.

@buyaa-n buyaa-n added the enhancement Product code improvement that does NOT require public API changes/additions label Jul 20, 2021
@bartonjs
Copy link
Member

bartonjs commented Jul 27, 2021

Video

We came up with a lot of cases where the fixer can cause surprising functional changes, so this really only applies when there's an indexer followed by a call to Remove with no other method calls in between (because any method call could have modified the dictionary). As a specific example, in #33800 (comment) if Foo throws then in the before case the dictionary is not modified, but in the after case it is.

The case of x = dict[key]; dict.Remove(key); isn't safe to replace, because the exception is lost.

Conclusion:

If we find a dictionary call to TryGetValue that is immediately followed by a call to Remove (and the dictionary and the key are both simple variable or parameter expressions) then suggest collapsing them to Remove(key, out T). ("immediate" definitely means that there are no method calls or delegate invocations between those two expressions)

  • Category: Performance
  • Severity: Information

@bartonjs bartonjs added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for review, it is NOT ready for implementation labels Jul 27, 2021
@carlossanlop carlossanlop added the help wanted [up-for-grabs] Good issue for external contributors label Sep 14, 2021
@steveberdy
Copy link
Contributor

@buyaa-n may I have this assigned to me? Thanks.

@buyaa-n buyaa-n assigned buyaa-n and steveberdy and unassigned buyaa-n Oct 18, 2021
@buyaa-n
Copy link
Contributor

buyaa-n commented Oct 18, 2021

Thank you @steveberdy, sorry for the delayed response, it is assigned to you now, JFYI there are several analyzer proposals for dictionary, their implementations preferred to be written in one place (with different diagnostic ID when needed). I see only one of them merged into repo please try to add your implementation to that

@buyaa-n buyaa-n removed api-approved API was approved in API review, it can be implemented help wanted [up-for-grabs] Good issue for external contributors labels Oct 26, 2021
@buyaa-n buyaa-n added the api-approved API was approved in API review, it can be implemented label Oct 29, 2021
@steveberdy steveberdy removed their assignment Sep 14, 2022
@steveberdy
Copy link
Contributor

I'm sorry. I don't really have time to make this analyzer. I've unassigned myself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-approved API was approved in API review, it can be implemented area-System.Collections code-analyzer Marks an issue that suggests a Roslyn analyzer code-fixer Marks an issue that suggests a Roslyn code fixer enhancement Product code improvement that does NOT require public API changes/additions help wanted [up-for-grabs] Good issue for external contributors
Projects
None yet
Development

No branches or pull requests

7 participants