CA1851: diversas enumerações da coleção IEnumerable
são possíveis
Property | Valor |
---|---|
ID da regra | CA1851 |
Título | Possíveis enumerações múltiplas da coleção IEnumerable |
Categoria | Desempenho |
Correção interruptiva ou sem interrupção | Sem interrupção |
Versão introduzida | .NET 7 |
Habilitado por padrão no .NET 8 | Não |
Causa
Detectadas várias enumerações de uma coleção IEnumerable
.
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 a execução adiada. A enumeração começa quando a coleção é passada para um método LINQ, como ElementAt, ou usada em um método para cada instrução. O resultado da enumeração não é calculado uma vez e armazenado em um cache, como Lazy.
Se a operação de enumeração em si for cara, por exemplo, uma consulta em um banco de dados, enumerações múltiplas serão prejudiciais ao desempenho do programa.
Se a operação de enumeração tiver efeitos colaterais, enumerações múltiplas poderão resultar em bugs.
Como corrigir violações
Se o tipo subjacente de sua coleção IEnumerable
for outro, como List
ou Array
, será seguro converter a coleção em seu 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 coleção IEnumerable
usar uma implementação baseada em iterador (por exemplo, se ela 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 System.Linq são incluídos no escopo de análise. Você pode adicionar métodos personalizados que enumeram um argumento IEnumerable
no escopo definindo a opção enumeration_methods
em um arquivo .editorconfig.
Você também pode adicionar métodos de cadeia LINQ personalizados (ou seja, os métodos pegam um argumento IEnumerable
e retornam uma nova instância IEnumerable
) ao escopo de análise definindo a opção linq_chain_methods
em um arquivo .editorconfig.
Configurar a suposição padrão de que métodos usam parâmetros IEnumerable
Por padrão, presumem-se que os métodos personalizados que aceitam um argumento IEnumerable
não enumeram o argumento. Você pode alterar isso definindo a opção assume_method_enumerates_parameters
em um arquivo .editorconfig.
Quando suprimir avisos
Será seguro suprimir esse aviso se o tipo subjacente da coleção IEnumerable
for algum outro tipo como List
ou Array
, ou se você tiver certeza de que os métodos que usam uma coleção IEnumerable
não o enumeram.
Suprimir um aviso
Para suprimir apenas uma violação, adicione diretivas de pré-processador ao arquivo de origem a fim de desabilitar e, em seguida, reabilitar 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 em um arquivo, uma pasta ou um projeto, defina a severidade como none
no arquivo de configuração.
[*.{cs,vb}]
dotnet_diagnostic.CA1851.severity = none
Para obter mais informações, confira Como suprimir avisos de análise de código.