CA1851: Possible multiple enumerations of IEnumerable collection
| Value | |
|---|---|
| Rule ID | CA1851 |
| Category | Performance |
| Fix is breaking or non-breaking | Non-breaking |
Cause
Detected multiple enumerations of an IEnumerable collection.
Rule description
Collection with IEnumerable or IEnumerable<T> type generated by many LINQ methods like Select or yield in C# or yield statement in Visual Basic has the ability to defer enumeration when it is generated. The enumeration will start as long as it is passed into enumeration LINQ methods like ElementAt or used in for each statement in C# or For Each..Next Statement in Visual Basic. The enumeration result is not calculated once and cached like Lazy.
If the enumeration operation itself is expensive, for example, a query into a database, multiple enumerations would be harmful to the performance of the program.
If the enumeration operation has side effects, multiple enumerations might result in bugs.
How to fix violations
If the underlying type of IEnumerable is some other type like List or Array, it is safe to convert the collection to its underlying type to fix the diagnostic.
Violation:
public void MyMethod(IEnumerable<int> input)
{
var count = input.Count();
foreach (var i in input) { }
}
Public Sub MyMethod(input As IEnumerable(Of Integer))
Dim count = input.Count()
For Each i In input
Next
End Sub
Fix:
public void MyMethod(IEnumerable<int> input)
{
// If the underlying type of 'input' is List<int>
var inputList = (List<int>)input;
var count = inputList.Count();
foreach (var i in inputList) { }
}
Public Sub MyMethod(input As IEnumerable(Of Integer))
' If the underlying type of 'input' is array
Dim inputArray = CType(input, Integer())
Dim count = inputArray.Count()
For Each i In inputArray
Next
End Sub
If the underlying type of the IEnumerable collection is an iterator-based implementation generated by LINQ methods like Select or yield in C# or yield statement in Visual Basic, you can fix the violation by converting and caching the collection to another type. However, this allocates extra memory.
For example:
Violation:
public void MyMethod()
{
var someStrings = GetStrings().Select(i => string.Concat("Hello"));
// It takes 2 * O(n) to complete 'Count()' and 'Last()' calls, where 'n' is the length of 'someStrings'.
var count = someStrings.Count();
var lastElement = someStrings.Last();
}
Public Sub MyMethod()
Dim someStrings = GetStrings().Select(Function(i) String.Concat(i, "Hello"))
' It takes 2 * O(n) to complete 'Count()' and 'Last()' calls, where 'n' is the length of 'someStrings'.
Dim count = someStrings.Count()
Dim lastElement = someStrings.Last()
End Sub
Fix:
public void MyMethod()
{
var someStrings = GetStrings().Select(i => string.Concat("Hello"));
// Cache it into an array.
// Note: This operation would allocate O(n) memory,
// and it takes O(n) time to finish, where 'n' is the length of 'someStrings'.
var someStringsArray = someStrings.ToArray()
// It takes 2 * O(1) to complete 'Count()' and 'Last()' calls.
var count = someStringsArray.Count();
var lastElement = someStringsArray.Last();
}
Public Sub MyMethod()
Dim someStrings = GetStrings().Select(Function(i) String.Concat(i, "Hello"))
' Cache it into an array.
' Note: This operation would allocate O(n) memory,
' and it takes O(n) time to finish, where 'n' is the length of 'someStrings'.
Dim someStringsArray = someStrings.ToArray()
' It takes 2 * O(1) to complete 'Count()' and 'Last()' calls.
Dim count = someStrings.Count()
Dim lastElement = someStrings.Last()
End Sub
Configure customized enumeration methods and LINQ chain methods
By default, all the methods in the System.Linq namespace are included in the analysis scope. You can add custom methods that enumerate an IEnumerable argument into the scope by setting the enumeration_methods option in an .editorconfig file.
You can also add customized LINQ chain methods (that is, methods take an IEnumerable argument and return a new IEnumerable instance) to the analysis scope by setting the linq_chain_methods option in an .editorconfig file.
Configure the default assumption of methods take IEnumerable parameters
By default, all customized methods that accept an IEnumerable argument are assumed not to enumerate the argument. You can change this by setting the assume_method_enumerates_parameters option in an .editorconfig file.
When to suppress warnings
It is safe to suppress this warning if the underlying type of the IEnumerable collection is some other type like List or Array, or if you're sure that methods that take an IEnumerable collection do not enumerate it.
Suppress a warning
If you just want to suppress a single violation, add preprocessor directives to your source file to disable and then re-enable the rule.
#pragma warning disable CA1851
// The code that's violating the rule is on this line.
#pragma warning restore CA1851
To disable the rule for a file, folder, or project, set its severity to none in the configuration file.
[*.{cs,vb}]
dotnet_diagnostic.CA1851.severity = none
To disable this entire category of rules, set the severity for the category to none in the configuration file.
[*.{cs,vb}]
dotnet_analyzer_diagnostic.category-Performance.severity = none
For more information, see How to suppress code analysis warnings.
See also
Povratne informacije
Pošalјite i prikažite povratne informacije za