CA1851 : Énumérations multiples possibles de la collection IEnumerable

Propriété Value
Identificateur de la règle CA1851
Titre Plusieurs énumérations possibles de la collection IEnumerable
Catégorie Performances
Le correctif est cassant ou non cassant Sans rupture
Version introduite .NET 7
Activé par défaut dans .NET 8 Non

Cause

Détection de plusieurs énumérations d’une collection IEnumerable.

Description de la règle

Une collection de type IEnumerable ou IEnumerable<T> a la possibilité de différer l’énumération lorsqu’elle est générée. De nombreuses méthodes LINQ, comme Select, utilisent une exécution différée. L’énumération commence lorsque la collection est passée dans une méthode d’énumération LINQ, comme ElementAt, ou utilisée dans une instruction for each. Le résultat de l’énumération n’est pas calculé une seule fois et mis en cache, comme pour Différé.

Si l’opération d’énumération elle-même est coûteuse, par exemple, une requête dans une base de données, plusieurs énumérations nuiraient aux performances du programme.

Si l’opération d’énumération a des effets secondaires, plusieurs énumérations peuvent entraîner des bogues.

Comment corriger les violations

Si le type sous-jacent de votre collection IEnumerable est un autre type, comme List ou Array, il est sûr de convertir la collection en son type sous-jacent pour corriger le diagnostic.

Violation :

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

Correctif :

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 le type sous-jacent de la collection IEnumerable utilise une implémentation avec itérateur (par exemple, si elle est générée par des méthodes LINQ comme Select ou à l’aide du mot clé yield), vous pouvez corriger la violation en matérialisant la collection. Toutefois, cela alloue de la mémoire supplémentaire.

Par exemple :

Violation :

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

Correctif :

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

Configurer des méthodes d’énumération personnalisées et des méthodes de chaîne LINQ

Par défaut, toutes les méthodes de l’espace de noms System.Linq sont incluses dans l’étendue d’analyse. Vous pouvez ajouter des méthodes personnalisées qui énumèrent un argument IEnumerable dans l’étendue en définissant l’option enumeration_methods dans un fichier .editorconfig.

Vous pouvez également ajouter des méthodes de chaîne LINQ personnalisées (autrement dit, les méthodes prennent un argument IEnumerable et retournent une nouvelle instance IEnumerable) à l’étendue d’analyse en définissant l’option linq_chain_methods dans un fichier .editorconfig.

Configurer l’hypothèse par défaut que les méthodes prennent des paramètres IEnumerable

Par défaut, toutes les méthodes personnalisées qui acceptent un argument IEnumerable sont supposées ne pas énumérer l’argument. Vous pouvez modifier cela en définissant l’option assume_method_enumerates_parameters dans un fichier .editorconfig.

Quand supprimer les avertissements

Il est sûr de supprimer cet avertissement si le type sous-jacent de la collection IEnumerable est un autre type comme List ou Array, ou si vous êtes sûr que les méthodes qui prennent une collection IEnumerable ne l’énumèrent pas.

Supprimer un avertissement

Si vous voulez supprimer une seule violation, ajoutez des directives de préprocesseur à votre fichier source pour désactiver et réactiver la règle.

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

Pour désactiver la règle sur un fichier, un dossier ou un projet, définissez sa gravité sur none dans le fichier de configuration.

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

Pour plus d’informations, consultez Comment supprimer les avertissements de l’analyse de code.

Voir aussi