Поделиться через


CA1851: возможные несколько перечислений IEnumerable коллекции

Свойство Значение
Идентификатор правила CA1851
Заголовок Возможные несколько перечислений IEnumerable коллекции
Категория Производительность
Исправление является критическим или не критическим Не критическое
Представленные версии .NET 7
Включен по умолчанию в .NET 9 No

Причина

Обнаружено несколько перечислений IEnumerable коллекции.

Описание правила

Коллекция типов IEnumerable или IEnumerable< T> имеет возможность отложить перечисление при его создании. Многие методы LINQ, такие как Select, используют отложенное выполнение. Перечисление начинается, когда коллекция передается в метод перечисления LINQ, например ElementAt, или используется в каждой инструкции. Результат перечисления не вычисляется один раз и кэшируется, например Lazy.

Если операция перечисления является дорогой, например запрос в базу данных, несколько перечислений повреждют производительность программы.

Если операция перечисления имеет побочные эффекты, несколько перечислений могут привести к ошибкам.

Устранение нарушений

Если базовый тип IEnumerable коллекции является другим типом, например List или Array, можно преобразовать коллекцию в его базовый тип, чтобы исправить диагностику.

Нарушение:

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

Исправление:

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

Если базовый тип IEnumerable коллекции использует реализацию на основе итератора (например, если она создается методами LINQ, например Select или с помощью ключевого слова получения ), можно устранить нарушение, материализуя коллекцию. Однако это выделяет дополнительную память.

Например:

Нарушение:

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

Исправление:

public void MyMethod()
{
    var someStrings = GetStrings().Select(i => string.Concat("Hello"));
    // Materialize 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"))
    ' Materialize 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

Настройка настраиваемых методов перечисления и методов LINQ chain

По умолчанию все методы в пространстве имен включены в System.Linq область анализа. Можно добавить настраиваемые методы, перечисляющие IEnumerable аргумент в область, задав enumeration_methods параметр в файле editorconfig .

Можно также добавить настраиваемые методы LINQ chain (т. е. методы принимают IEnumerable аргумент и возвращают новый IEnumerable экземпляр) в область анализа, задав linq_chain_methods параметр в файле editorconfig .

Настройка допущения по умолчанию методов принимает IEnumerable параметры

По умолчанию предполагается, что все настраиваемые методы, принимаюющие IEnumerable аргумент, не перечисляют аргумент. Это можно изменить, задав assume_method_enumerates_parameters параметр в файле editorconfig .

Когда лучше отключить предупреждения

Это предупреждение безопасно подавлять, если базовый тип IEnumerable коллекции является другим типом, например List или Array, если вы уверены, что методы, которые IEnumerable принимают коллекцию, не перечисляют его.

Отключение предупреждений

Если вы просто хотите отключить одно нарушение, добавьте директивы препроцессора в исходный файл, чтобы отключить и повторно включить правило.

#pragma warning disable CA1851
// The code that's violating the rule is on this line.
#pragma warning restore CA1851

Чтобы отключить правило для файла, папки или проекта, задайте его серьезность none в файле конфигурации.

[*.{cs,vb}]
dotnet_diagnostic.CA1851.severity = none

Дополнительные сведения см. в разделе Практическое руководство. Скрытие предупреждений анализа кода.

См. также