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<K, V>.TryAddValue(key) over guarded Add(key) #33799

Closed
Tracked by #57797
terrajobst opened this issue Mar 19, 2020 · 8 comments · Fixed by dotnet/roslyn-analyzers#6199
Closed
Tracked by #57797
Assignees
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
Milestone

Comments

@terrajobst
Copy link
Member

!Dictionary<K, V>.ContainsKey(key) followed by Dictionary<K, V>.Add(key) can be combined into a single TryAdd call.

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
@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added the untriaged New issue has not been triaged by the area owner label 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

@terrajobst terrajobst removed the untriaged New issue has not been triaged by the area owner label Mar 21, 2020
@carlossanlop
Copy link
Member

Suggested severity: Info

Cases to cover:

using System;
using System.Collections.Generic;

namespace MyNamespace
{
    public static class MyClass
    {
        static void Main()
        {
            Dictionary<int, int> d = new();

            // A) Check the negative returned value
            // Before
            if (!d.ContainsKey(5))
            {
                d.Add(5, 6);
            }

            // After
            d.TryAdd(5, 6);


            // B) Check the negative returned value and preserve existing logic inside the if body
            // Before
            if (!d.ContainsKey(5))
            {
                d.Add(5, 6);
                Console.WriteLine($"Value: {d[5]}");
            }

            // After
            if (d.TryAdd(5, 6))
            {
                Console.WriteLine($"Value: {d[5]}");
            }

            // C) Check the positive returned value and preserve existing logic in the else bodyif there's one
            // Before
            if (d.ContainsKey(5))
            {
                Console.WriteLine($"Value existed: {d[5]}");
            }
            else
            {
                d.Add(5, 6);
                Console.WriteLine($"Value added: {d[5]}");
            }

            // After
            if (!d.TryAdd(5, 6))
            {
                Console.WriteLine($"Value existed: {d[5]}");
            }
            else
            {
                Console.WriteLine($"Value added: {d[5]}");
            }
        }
    }
}

@carlossanlop carlossanlop 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 labels Jan 15, 2021
@GrabYourPitchforks
Copy link
Member

I'd suggest enabling this analyzer only for scenarios where the associated value is: (a) a literal; or (b) already fully hydrated before the call to ContainsKey. Otherwise you risk a perf regression in scenarios like the following.

if (!dict.ContainsKey(key))
{
    dict[key] = ComputeExpensiveValue(); // expensive computation guarded within ContainsKey check
}

In theory you could take a factory argument to perform the expensive computation, but now we'd be making callers understand lambdas and closures when all they wanted to do was a simple dictionary access, and I don't think that's a good trade-off for the majority of our developers.

@terrajobst terrajobst 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 Feb 4, 2021
@terrajobst
Copy link
Member Author

terrajobst commented Feb 4, 2021

Video

  • Looks good
  • Please consider the comment here.

@CollinAlpert
Copy link
Contributor

CollinAlpert commented Feb 7, 2021

I'd like to do this one. @carlossanlop

@carlossanlop
Copy link
Member

carlossanlop commented Jul 6, 2022

@buyaa-n @CollinAlpert has a PR been merged fixing this? I see there are 3 related issues and more than one related PR but I'm unsure which issues got fixed.

@buyaa-n
Copy link
Contributor

buyaa-n commented Jul 6, 2022

Yes this one is fixed

@buyaa-n buyaa-n closed this as completed Jul 6, 2022
@buyaa-n
Copy link
Contributor

buyaa-n commented Jul 22, 2022

Oops, mistaken with #33798, it is not done, reopening

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
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
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants