Share via


CA1851: Möjliga flera uppräkningar av IEnumerable samlingen

Property Värde
Regel-ID CA1851
Rubrik Möjliga flera uppräkningar av IEnumerable samlingen
Kategori Prestanda
Korrigeringen är icke-bakåtkompatibel Icke-icke-bryta
Introducerad version .NET 7
Aktiverad som standard i .NET 8 Nej

Orsak

Flera uppräkningar av en IEnumerable samling har identifierats.

Regelbeskrivning

En samling av typen IEnumerable eller IEnumerable<T> har möjlighet att skjuta upp uppräkning när den genereras. Många LINQ-metoder, till exempel Select, använder uppskjuten körning. Uppräkning startar när samlingen skickas till en LINQ-uppräkningsmetod, till exempel ElementAt, eller används i en för varje -instruktion. Uppräkningsresultatet beräknas inte en gång och cachelagras, till exempel Lazy.

Om själva uppräkningsåtgärden är dyr, till exempel en fråga till en databas, skulle flera uppräkningar skada programmets prestanda.

Om uppräkningsåtgärden har biverkningar kan flera uppräkningar resultera i buggar.

Så här åtgärdar du överträdelser

Om den underliggande typen av samling IEnumerable är någon annan typ, till exempel List eller Array, är det säkert att konvertera samlingen till dess underliggande typ för att åtgärda diagnostiken.

Överträdelse:

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

Lösningen

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

Om den underliggande typen av IEnumerable samling använder en iteratorbaserad implementering (till exempel om den genereras av LINQ-metoder som Select eller med nyckelordet yield ) kan du åtgärda överträdelsen genom att materialisera samlingen. Detta allokerar dock extra minne.

Till exempel:

Överträdelse:

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

Lösningen

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

Konfigurera anpassade uppräkningsmetoder och LINQ-kedjemetoder

Som standard ingår alla metoder i System.Linq namnområdet i analysomfånget. Du kan lägga till anpassade metoder som räknar upp ett IEnumerable argument i omfånget genom att ange enumeration_methods alternativet i en .editorconfig-fil .

Du kan också lägga till anpassade LINQ-kedjemetoder (dvs. metoder tar ett IEnumerable argument och returnerar en ny IEnumerable instans) till analysomfånget linq_chain_methods genom att ange alternativet i en .editorconfig-fil .

Konfigurera standardantagandet för metoder tar IEnumerable parametrar

Som standard antas alla anpassade metoder som accepterar ett IEnumerable argument inte räkna upp argumentet. Du kan ändra detta genom att ange alternativet assume_method_enumerates_parameters i en .editorconfig-fil .

När du ska ignorera varningar

Det är säkert att ignorera den här varningen om den underliggande typen av IEnumerable samlingen är någon annan typ som List eller Array, eller om du är säker på att metoder som tar en IEnumerable samling inte räknar upp den.

Ignorera en varning

Om du bara vill förhindra en enda överträdelse lägger du till förprocessordirektiv i källfilen för att inaktivera och aktiverar sedan regeln igen.

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

Om du vill inaktivera regeln för en fil, mapp eller ett projekt anger du dess allvarlighetsgrad till none i konfigurationsfilen.

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

Mer information finns i Så här utelämnar du kodanalysvarningar.

Se även