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:
- 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. - Return an
ImmutableList<int>
orImmutableArray<int>
(or perhaps something else) viayourList.ToImmutableList()
to make the analyzer happy with a concrete type while also returning something that is immutable likeIEnumerable<int>
would be. - Just return
yourList.AsEnumerable()
to explicitly return theList<int>
as anIEnumerable<int>
. The message will go away because you are explicitly accepting whatever performance hit the message is intended to protect against anyway.