dotnet / roslyn-analyzers

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

CA1859 Questionable trigger for List and collection expression for empty enumerable

ArturDorochowicz opened this issue · comments

Analyzer

Diagnostic ID: CA1859: Use concrete types when possible for improved performance

Analyzer source

SDK: Built-in CA analyzers in .NET 5 SDK or later

Version: SDK 8.0.202

Describe the bug

Consider code like this:

private IEnumerable<int> Method(bool condition) {
  if (!condition) {
    return [];
  }

  return new List<int>(); // something returning List, e.g. EF query with .ToList() 
}

For some reason CA1859 triggers for Method. But changing the return type from IEnumerable<int> to List<int> will cause [] to allocate a list instead of returning Array.Empty (or is it Enumerable.Empty here).

Steps To Reproduce

dotnet new classlib
dotnet new editorconfig --empty
// .editorconfig
root = true
[*.cs]
dotnet_diagnostic.CA1859.severity = warning
// Class1.cs
public class Class1 {
  IEnumerabe<int> Method(bool condition) {
    if (!condition) {
      return [];
    }
    return new List<int>();
  }
}
$> dotnet build
MSBuild version 17.9.6+a4ecab324 for .NET
  Determining projects to restore...
  All projects are up-to-date for restore.
/src/Class1.cs(5,19): warning CA1859: Change return type of method 'Method' from 'System.Collections.Generic.IEnumerable<int>' to 'System.Collections.Generic.List<int>' for improved performance (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1859) [/src/src.csproj]
  src -> /src/bin/Debug/net8.0/src.dll

Build succeeded.

/src/Class1.cs(5,19): warning CA1859: Change return type of method 'Method' from 'System.Collections.Generic.IEnumerable<int>' to 'System.Collections.Generic.List<int>' for improved performance (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1859) [/src/src.csproj]
    1 Warning(s)
    0 Error(s)

Time Elapsed 00:00:01.47

Expected behavior

I think that CA1859 should not trigger in the above example code.

Actual behavior

CA1859 triggers. Following the analyzer leads to potentially worse performance.

Additional context

First of all, I think this rule is probably unnecessary or maybe just a little too aggressive. And your issue might be an example of why that is.

With that being said, it is behaving correctly here, as far as I can tell. There is no singleton for List so it can't return that anyway, so that is fine, right? There's no reason it has to be a singleton. It is just ideal when one exists.

So I think you might be looking at this the wrong way, no offense. This all comes down to what your intent is or what your needs are.

If you really are returning an empty list at the end there, then just change new List<int>() to [] and then it will return the same singleton as above in the if statement.

But then why does that condition even exist?

I imagine in your real use scenario, you aren't returning an empty List, is that right? In that case I think you have a couple of options:

  1. You might be able to use an IEnumerable<int> and methods like .Union() or .Append() / .Prepend() and/or collection expressions. That should get rid of the message, but also might give you the benefits of deferred execution.
  2. Return an ImmutableList<int> or ImmutableArray<int> (or perhaps something else) via yourList.ToImmutableList() to make the analyzer happy with a concrete type while also returning something that is immutable like IEnumerable<int> would be.
  3. Just return yourList.AsEnumerable() to explicitly return the List<int> as an IEnumerable<int>. The message will go away because you are explicitly accepting whatever performance hit the message is intended to protect against anyway.