代码之家  ›  专栏  ›  技术社区  ›  Mike Hofer

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

  •  2
  • Mike Hofer  · 技术社区  · 9 年前

    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 那么,这如何解决任何问题,或以任何方式提高性能?

    1 回复  |  直到 9 年前
        1
  •  4
  •   Rob    9 年前

    因为你可以这样写:

    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 ,你也在做计算。在这种情况下,工作并不多,为什么要做得比你需要的多(而且这是非常平常的工作)。