Skip to content

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

Open

Description

@terrajobst

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

Activity

  1. added this to the Future milestone on Mar 20, 2020
  2. jeffhandley commented on Mar 21, 2020

    @jeffhandley
    Member

    Estimates:

    • Analyzer: Medium
    • Fixer: Medium
  3. carlossanlop commented on Jan 8, 2021

    @carlossanlop
    Contributor

    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);
                }
            }​​
  4. added
    needs-further-triageIssue has been initially triaged, but needs deeper consideration or reconsideration
    on Jan 8, 2021
  5. added
    api-ready-for-reviewAPI is ready for review, it is NOT ready for implementation
    and removed
    api-suggestionEarly API idea and discussion, it is NOT ready for implementation
    needs-further-triageIssue has been initially triaged, but needs deeper consideration or reconsideration
    on Feb 23, 2021
  6. buyaa-n commented on Jun 3, 2021

    @buyaa-n
  7. added
    enhancementProduct code improvement that does NOT require public API changes/additions
    on Jul 20, 2021
  8. bartonjs commented on Jul 27, 2021

    @bartonjs
    Member

    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
  9. added
    api-approvedAPI was approved in API review, it can be implemented
    and removed
    api-ready-for-reviewAPI is ready for review, it is NOT ready for implementation
    on Jul 27, 2021
  10. steveberdy commented on Oct 15, 2021

    @steveberdy
    Contributor

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

  11. buyaa-n commented on Oct 18, 2021

    @buyaa-n
    Contributor

    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

  12. added
    api-approvedAPI was approved in API review, it can be implemented
    and removed
    api-approvedAPI was approved in API review, it can be implemented
    help wanted[up-for-grabs] Good issue for external contributors
    on Oct 26, 2021
  13. removed their assignment
    on Sep 14, 2022
  14. steveberdy commented on Sep 14, 2022

    @steveberdy
    Contributor

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

  15. added
    help wanted[up-for-grabs] Good issue for external contributors
    on Sep 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-approvedAPI was approved in API review, it can be implementedarea-System.Collectionscode-analyzerMarks an issue that suggests a Roslyn analyzercode-fixerMarks an issue that suggests a Roslyn code fixerenhancementProduct code improvement that does NOT require public API changes/additionshelp wanted[up-for-grabs] Good issue for external contributors

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions