Partilhar via


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

Propriedade valor
ID da regra CA1851
Cargo 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 8 Não

Causa

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 namespace são incluídos no System.Linq escopo da análise. Você pode adicionar métodos personalizados que enumeram um argumento no escopo definindo a opção em um IEnumerablearquivo .editorconfig.enumeration_methods

Você também pode adicionar métodos de cadeia LINQ personalizados (ou seja, os métodos usam um argumento e retornam uma nova IEnumerable instância) ao escopo da análise definindo a linq_chain_methods opção em um IEnumerablearquivo .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 , ou Arrayse 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 para um arquivo, pasta ou projeto, defina sua severidade 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