CA1851: Mögliche mehrere Enumerationen der „IEnumerable“-Auflistung

Eigenschaft Wert
Regel-ID CA1851
Titel Mögliche mehrere Enumerationen der IEnumerable-Auflistung
Kategorie Leistung
Fix führt oder führt nicht zur Unterbrechung Nicht unterbrechend
Eingeführt in Version .NET 7
Standardmäßig in .NET 8 aktiviert Nein

Ursache

Es wurden mehrere Enumerationen einer IEnumerable-Auflistung erkannt.

Regelbeschreibung

Eine Auflistung vom Typ IEnumerable oder IEnumerable<T> hat die Möglichkeit, die Enumeration zurückzustellen, wenn sie generiert wird. Viele LINQ-Methoden wie beispielsweise Select verwenden die verzögerte Ausführung. Die Enumeration beginnt, wenn die Auflistung an eine LINQ-Enumerationsmethode wie ElementAt übergeben oder in einer for each-Anweisung verwendet wird. Das Enumerationsergebnis wird nicht einmal berechnet und zwischengespeichert, wie z. B. Lazy.

Wenn der Enumerationsvorgang selbst aufwendig ist, z. B. eine Abfrage in einer Datenbank, würden mehrere Enumerationen die Leistung des Programms beeinträchtigen.

Wenn der Enumerationsvorgang Nebenwirkungen hat, können mehrere Enumerationen zu Fehlern führen.

Behandeln von Verstößen

Wenn der zugrunde liegende Typ Ihrer IEnumerable-Auflistung ein anderer Typ ist, z. B. List oder Array, ist es sicher, die Auflistung in den zugrunde liegenden Typ zu konvertieren, um die Diagnose zu korrigieren.

Verstoß:

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

Korrektur:

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

Wenn der zugrunde liegende Typ der IEnumerable-Auflistung eine iteratorbasierte Implementierung verwendet (z. B. wenn sie von LINQ-Methoden wie Select oder mithilfe der Schlüsselworts yield generiert wird), können Sie den Verstoß beheben, indem Sie die Auflistung materialisieren. Dadurch wird jedoch zusätzlicher Arbeitsspeicher zugewiesen.

Beispiel:

Verstoß:

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

Korrektur:

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

Konfigurieren von angepassten Enumerationsmethoden und LINQ-Kettenmethoden

Standardmäßig sind alle Methoden im Namespace System.Linq im Analyseumfang enthalten. Sie können benutzerdefinierte Methoden hinzufügen, die ein IEnumerable-Argument in den Umfang enumerieren, indem Sie die Option enumeration_methods in einer .editorconfig-Datei festlegen.

Sie können auch angepasste LINQ-Kettenmethoden (d. h. Methoden, die ein IEnumerable-Argument akzeptieren und eine neue IEnumerable-Instanz zurückgeben) zum Analysebereich hinzufügen, indem Sie die Option linq_chain_methods in einer .editorconfig--Datei festlegen.

Konfigurieren der Standardannahme, dass Methoden IEnumerable-Parameter akzeptieren

Standardmäßig wird bei allen angepassten Methoden, die ein IEnumerable-Argument akzeptieren, angenommen, dass sie das Argument nicht enumerieren. Diese Einstellung können Sie ändern, indem Sie die Option assume_method_enumerates_parameters in einer .editorconfig--Datei festlegen.

Wann sollten Warnungen unterdrückt werden?

Es ist sicher, diese Warnung zu unterdrücken, wenn der zugrunde liegende Typ der IEnumerable-Auflistung ein anderer Typ wie z. B. List oder Array ist oder wenn Sie sicher sind, dass Methoden, die eine IEnumerable-Auflistung akzeptieren, diese nicht enumerieren.

Unterdrücken einer Warnung

Um nur eine einzelne Verletzung zu unterdrücken, fügen Sie der Quelldatei Präprozessoranweisungen hinzu, um die Regel zu deaktivieren und dann wieder zu aktivieren.

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

Um die Regel für eine Datei, einen Ordner oder ein Projekt zu deaktivieren, legen Sie den Schweregrad in der Konfigurationsdatei auf none fest.

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

Weitere Informationen finden Sie unter Vorgehensweise: Unterdrücken von Codeanalyse-Warnungen.

Siehe auch