CA1851: Posibles enumeraciones múltiples de la colección IEnumerable

Propiedad Value
Identificador de la regla CA1851
Título Posibles enumeraciones múltiples de IEnumerable colección
Categoría Rendimiento
La corrección es problemática o no problemática Poco problemático
Versión introducida .NET 7
Habilitado de forma predeterminada en .NET 8 No

Causa

Se detectaron varias enumeraciones de una colección IEnumerable.

Descripción de la regla

Una colección de tipo IEnumerable o IEnumerable<T> tiene la capacidad de aplazar la enumeración cuando se genera. Muchos métodos LINQ, como Select, usan la ejecución diferida. La enumeración se inicia cuando la colección se pasa a un método de enumeración LINQ, como ElementAt, o cuando se usa en una instrucción for each. El resultado de la enumeración no se calcula una vez y se almacena en caché, como Lazy.

Si la propia operación de enumeración es costosa, por ejemplo, una consulta en una base de datos, varias enumeraciones perjudicarían el rendimiento del programa.

Si la operación de enumeración tiene efectos secundarios, es posible que varias enumeraciones produzcan errores.

Cómo corregir infracciones

Si el tipo subyacente de la colección IEnumerable es otro tipo, como List o Array, es seguro convertir la colección en su tipo subyacente para corregir el diagnóstico.

Infracción:

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

Corrección:

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

Si el tipo subyacente de la colección IEnumerable usa una implementación basada en iteradores (por ejemplo, si se genera mediante métodos LINQ como Select o mediante la palabra clave yield), puede corregir la infracción materializando la colección. No obstante, esto asigna memoria adicional.

Por ejemplo:

Infracción:

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

Corrección:

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

Configuración de métodos de enumeración personalizados y métodos de cadena LINQ

De forma predeterminada, todos los métodos del espacio de nombres System.Linq se incluyen en el ámbito de análisis. Puede agregar métodos personalizados que enumeren un argumento IEnumerable en el ámbito estableciendo la opción enumeration_methods en un archivo .editorconfig.

También puede agregar métodos de cadena LINQ personalizados (es decir, los métodos toman un argumento IEnumerable y devuelven una nueva instancia IEnumerable) al ámbito de análisis estableciendo la opción linq_chain_methods en un archivo .editorconfig.

La configuración de la suposición predeterminada de métodos toma parámetros IEnumerable

De forma predeterminada, se supone que todos los métodos personalizados que aceptan un argumento IEnumerable no enumeran el argumento. Puede cambiarlo estableciendo la opción assume_method_enumerates_parameters en un archivo .editorconfig.

Cuándo suprimir las advertencias

Es seguro suprimir esta advertencia si el tipo subyacente de la colección IEnumerable es de otro tipo como List o Array, o si está seguro de que los métodos que toman una colección IEnumerable no lo enumeran.

Supresión de una advertencia

Si solo quiere suprimir una única infracción, agregue directivas de preprocesador al archivo de origen para deshabilitar y volver a habilitar la regla.

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

Para deshabilitar la regla de un archivo, una carpeta o un proyecto, establezca su gravedad en none del archivo de configuración.

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

Para obtener más información, consulte Procedimiento para suprimir advertencias de análisis de código.

Consulte también