ELI5:将 IEnumerable 转换为列表或数组如何避免 IEnumerable 的多次枚举的性能损失?
ELI5: How does converting an IEnumerable to a list or array avoid the performance penalty of multiple enumeration of IEnumerable?
Resharper 不断抱怨:可能对 IEnumerable 进行多重枚举。例如:
private int ParseLoanNumber(IEnumerable<string> lines)
{
var loanNumber = 0;
var item = lines.FirstOrDefault(l => l.StartsWith(" LN# 00"));
if (item != null)
{
loanNumber = item.ParseInt(8, 10).GetValueOrDefault();
}
else
{
item = lines.FirstOrDefault(l => l.StartsWith(" LOAN-NO (CONT'D) 00"));
if (item != null)
{
loanNumber = item.ParseInt(19, 10).GetValueOrDefault();
}
}
// Yada yada...
}
推荐的解决方案是将 enumerable
转换为 list
或 array
,然后对其进行迭代。
这让我很困惑。您仍然会枚举一些东西,并且两种类型(数组和列表)都实现 IEnumerable
。那么这如何解决任何问题或以任何方式提高性能?
因为你可以这样写:
public IEnumerable<int> GetNumbersSlowly()
{
for (var i = 0; i < 100; i++)
{
Thread.Sleep(10000); //Or retrieve from a website, etc
yield return i;
}
}
如果你这样使用它:
var numbers = GetNumbersSlowly();
foreach(var number in numbers) {
//Do something
}
foreach(var number in numbers) {
//Do something
}
这意味着每个数字完成的工作(睡眠)两次。计算可枚举一次并将其存储在数组或列表中意味着您确定没有对 return 项目进行额外处理。
由于您正在接受IEnumerable<string>
,您确实不知道来电者没有进行上述操作。
如果您认为我的示例可能很少见或属于边缘情况,它也适用于以下情况:
var someSource = new List<int> { 1, 2, 3, 4, 5 };
var numbers = someSource.Select(s => s * 100000);
现在每次迭代 numbers
时,您也在重新进行计算。在这种情况下,它的工作量并不大,为什么要做的比你需要的多(而且它是非常重要的工作并不少见)。
Resharper 不断抱怨:可能对 IEnumerable 进行多重枚举。例如:
private int ParseLoanNumber(IEnumerable<string> lines)
{
var loanNumber = 0;
var item = lines.FirstOrDefault(l => l.StartsWith(" LN# 00"));
if (item != null)
{
loanNumber = item.ParseInt(8, 10).GetValueOrDefault();
}
else
{
item = lines.FirstOrDefault(l => l.StartsWith(" LOAN-NO (CONT'D) 00"));
if (item != null)
{
loanNumber = item.ParseInt(19, 10).GetValueOrDefault();
}
}
// Yada yada...
}
推荐的解决方案是将 enumerable
转换为 list
或 array
,然后对其进行迭代。
这让我很困惑。您仍然会枚举一些东西,并且两种类型(数组和列表)都实现 IEnumerable
。那么这如何解决任何问题或以任何方式提高性能?
因为你可以这样写:
public IEnumerable<int> GetNumbersSlowly()
{
for (var i = 0; i < 100; i++)
{
Thread.Sleep(10000); //Or retrieve from a website, etc
yield return i;
}
}
如果你这样使用它:
var numbers = GetNumbersSlowly();
foreach(var number in numbers) {
//Do something
}
foreach(var number in numbers) {
//Do something
}
这意味着每个数字完成的工作(睡眠)两次。计算可枚举一次并将其存储在数组或列表中意味着您确定没有对 return 项目进行额外处理。
由于您正在接受IEnumerable<string>
,您确实不知道来电者没有进行上述操作。
如果您认为我的示例可能很少见或属于边缘情况,它也适用于以下情况:
var someSource = new List<int> { 1, 2, 3, 4, 5 };
var numbers = someSource.Select(s => s * 100000);
现在每次迭代 numbers
时,您也在重新进行计算。在这种情况下,它的工作量并不大,为什么要做的比你需要的多(而且它是非常重要的工作并不少见)。