Partilhar via


CA1851: Possíveis enumerações múltiplas de IEnumerable coleção

Property valor
ID da regra CA1851
Título Possíveis enumerações múltiplas de IEnumerable coleção
Categoria Desempenho
A correção está quebrando ou não quebrando Sem quebra
Versão introduzida .NET 7
Habilitado por padrão no .NET 9 Não

Motivo

Detetadas várias enumerações de uma IEnumerable coleção.

Descrição da regra

Uma coleção do tipo IEnumerable ou IEnumerable<T> tem a capacidade de adiar a enumeração quando ela é gerada. Muitos métodos LINQ, como Select, usam execução adiada. A enumeração começa quando a coleção é passada para um método de enumeração LINQ, como ElementAt, ou usada em um para cada instrução. O resultado da enumeração não é calculado uma vez e armazenado em cache, como Lazy.

Se a operação de enumeração em si for cara, por exemplo, uma consulta em um banco de dados, várias enumerações prejudicariam o desempenho do programa.

Se a operação de enumeração tiver efeitos colaterais, várias enumerações podem resultar em bugs.

Como corrigir violações

Se o tipo subjacente da sua IEnumerable coleção for algum outro tipo, como List ou Array, é seguro converter a coleção para o tipo subjacente para corrigir o diagnóstico.

Violação:

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

Correção:

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

Se o tipo subjacente da IEnumerable coleção usar uma implementação baseada em iterador (por exemplo, se for gerada por métodos LINQ como Select ou usando a palavra-chave yield ), você poderá corrigir a violação materializando a coleção. No entanto, isso aloca memória extra.

Por exemplo:

Violação:

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

Correção:

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

Configurar métodos de enumeração personalizados e métodos de cadeia LINQ

Por padrão, todos os métodos no System.Linq namespace são incluídos no escopo da análise. Você pode adicionar métodos personalizados que enumeram um IEnumerable argumento no escopo definindo a enumeration_methods opção em um arquivo .editorconfig .

Você também pode adicionar métodos de cadeia LINQ personalizados (ou seja, os métodos usam um IEnumerable argumento e retornam uma nova IEnumerable instância) ao escopo da análise definindo a linq_chain_methods opção em um arquivo .editorconfig .

Configurar a suposição padrão de métodos tomar IEnumerable parâmetros

Por padrão, todos os métodos personalizados que aceitam um IEnumerable argumento são assumidos para não enumerar o argumento. Você pode alterar isso definindo a assume_method_enumerates_parameters opção em um arquivo .editorconfig .

Quando suprimir avisos

É seguro suprimir esse aviso se o tipo subjacente da coleção for algum outro tipo como List ou Array, ou se você tiver certeza de que os IEnumerable métodos que usam uma IEnumerable coleção não a enumeram.

Suprimir um aviso

Se você quiser apenas suprimir uma única violação, adicione diretivas de pré-processador ao seu arquivo de origem para desativar e, em seguida, reativar a regra.

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

Para desabilitar a regra de um arquivo, pasta ou projeto, defina sua gravidade como none no arquivo de configuração.

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

Para obter mais informações, consulte Como suprimir avisos de análise de código.

Consulte também