CA1851:集合的 IEnumerable 可能多個列舉

屬性
規則識別碼 CA1851
標題 集合的 IEnumerable 可能多個列舉
類別 效能
修正程式是中斷或非中斷 不中斷
引進的版本 .NET 7
預設在 .NET 8 中啟用 No

原因

偵測到集合的 IEnumerable 多個列舉。

檔案描述

IEnumerable 或 IEnumerable< T> 類型的集合能夠在產生列舉時延遲列舉。 許多 LINQ 方法,例如 Select,都會使用延後執行。 列舉會在將集合傳遞至 LINQ 列舉方法時啟動,例如 ElementAt,或用於每個語句。 列舉結果不會計算一次並快取,例如 延遲

例如,如果列舉作業本身的成本很高,對資料庫的查詢,則多個列舉會損害程式的效能。

如果列舉作業有副作用,則多個列舉可能會導致 Bug。

如何修正違規

如果集合 IEnumerable 的基礎類型是某些其他類型,例如 ListArray,則安全地將集合轉換成其基礎類型,以修正診斷。

違規

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

修正

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

如果集合的基礎類型 IEnumerable 使用反覆運算器型實作(例如,如果它是由LINQ Select 方法所產生,或是使用 yield 關鍵詞所產生),您可以藉由具體化集合來修正違規。 不過,這會配置額外的記憶體。

例如:

違規

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

修正

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

設定自定義列舉方法和LINQ鏈結方法

根據預設,命名空間中的所有 System.Linq 方法都會包含在分析範圍中。 您可以藉由在 .editorconfig 檔案中設定 enumeration_methods 選項,將列舉IEnumerable自變數的自定義方法新增至範圍。

您也可以藉由在 .editorconfig 檔案中設定 linq_chain_methods 選項,將自定義的 LINQ 鏈結方法(也就是方法接受IEnumerable自變數並傳回新的IEnumerable實例)新增至分析範圍。

設定方法採用 IEnumerable 參數的預設假設

根據預設,會假設接受 IEnumerable 自變數的所有自定義方法都不會列舉自變數。 您可以在 .editorconfig 檔案中設定 assume_method_enumerates_parameters 選項,以變更此專案。

隱藏警告的時機

如果集合的基礎IEnumerable類型是 或 Array之類的其他類型List,或如果您確定採用IEnumerable集合的方法未列舉,則隱藏這個警告是安全的。

隱藏警告

如果您只想要隱藏單一違規,請將預處理器指示詞新增至原始程式檔以停用,然後重新啟用規則。

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

若要停用檔案、資料夾或項目的規則,請在組態檔中將其嚴重性設定為 。none

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

如需詳細資訊,請參閱 如何隱藏程式代碼分析警告

另請參閱