ELI5:如何将IEnumerable转换为列表或数组避免多次枚举IEnumerable的性能损失?

时间:2016-03-31 22:50:03

标签: c# resharper ienumerable enumeration

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转换为listarray,并对其进行迭代。

这令我感到困惑。您仍将枚举某些内容,两种类型(数组和列表)都实现IEnumerable。那么它如何解决任何问题,或者以任何方式改善性能呢?

1 个答案:

答案 0 :(得分:4)

因为你可以这样写:

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 
}

这意味着每个号码完成的工作(睡眠)两次。评估可枚举一次并将其存储在数组或列表中意味着您确定没有额外的处理来返回项目。

由于你正在使用IEnumerable<string>,你真的不知道调用者没有完成上述操作。

如果您认为我的示例可能很少或是边缘情况,它也适用于这样的事情:

var someSource = new List<int> { 1, 2, 3, 4, 5 };
var numbers = someSource.Select(s => s * 100000);

现在,每次迭代numbers时,您都会重新进行计算。在这种情况下,它没有多少工作,为什么它比你需要的更多(并且它是非常重要的工作并不罕见。)