CA1851:可能多次枚举了 IEnumerable
集合
属性 | 值 |
---|---|
规则 ID | CA1851 |
标题 | 可能多次枚举了 IEnumerable 集合 |
类别 | “性能” |
修复是中断修复还是非中断修复 | 非中断 |
引入的版本 | .NET 7 |
在 .NET 9 中默认启用 | 否 |
原因
检测到集合 IEnumerable
的多次枚举。
规则说明
IEnumerable 或 IEnumerable<T> 类型的集合能够在生成枚举时延迟该枚举。 许多 LINQ 方法(例如 Select)使用延迟执行。 当集合传递到 LINQ 枚举方法(如 ElementAt)或在 for each 语句中使用时,枚举开始。 枚举结果不会像 Lazy 一样计算一次并缓存。
如果枚举操作本身成本高昂(例如,对数据库进行查询),那么多次枚举会对程序的性能有害。
如果枚举操作具有副作用,则多次枚举可能会导致 bug。
如何解决冲突
如果 IEnumerable
集合的基础类型是其他某个类型(例如 List
或 Array
),则可以安全地将集合转换为其基础类型以修复诊断出的问题。
冲突:
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
集合的基础类型使用基于迭代器的实现(例如,如果它是由 Select 等 LINQ 方法生成的或通过使用 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
集合的基础类型是其他某个类型(例如 List
或 Array
),或者你确定采用 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
有关详细信息,请参阅如何禁止显示代码分析警告。