当索引比 C# 中的普通数组或列表慢得多时,为什么 IEnumerable 是可迭代列表的推荐数据类型?

Uch*_*tah 0 c# performance types c#-4.0 .net-core

我现在才学习 C# 几个月(新工作)。我对一些循环模式进行了粗略的速度测试,有些东西引起了我的注意。

int[] myArr = new int[] { 11, 12, 13, 14, 6, 22, 45, 13 };
IEnumerable<int> myEnum = myArr;

DateTime start = DateTime.Now;
int enumSum = myEnum.Sum();
Console.WriteLine("time using IEnum.Sum(): " + (DateTime.Now - start).TotalSeconds);
Console.WriteLine("using IEnum.Sum(): " + enumSum);
Run Code Online (Sandbox Code Playgroud)

平均而言, while 和 foreach 循环的性能远远优于其对应循环(当我直接操作数组时)。

while (idx < myArr.Length)
{
    total += myArr[idx];
    idx++;
}
Console.WriteLine("time using while loop: " + (DateTime.Now - start).TotalSeconds);
Console.WriteLine("using while loop: " + total);
Run Code Online (Sandbox Code Playgroud)

然而,由于 IEnumerable 索引不能直接引用,我尝试使用 ElementAt() 方法,差异令人难以置信。

while (idx < myEnum.Count())
{
    total += myEnum.ElementAt(idx);
    idx++;
}
Console.WriteLine("time using while loop: " + (DateTime.Now - start).TotalSeconds);
Console.WriteLine("using while loop: " + total);
Run Code Online (Sandbox Code Playgroud)

使用内置 IEnumerable.Sum():使用 IEnum.Sum() 的时间:0.005396

直接循环数组:使用while循环的时间:4E-07

循环 IEnumerable:使用 while 循环的时间:0.0011301

我知道 IEnumerable 有一个 ToArray() 方法,但真的想了解为什么 IEnumerable 是在 C# 中表示可迭代数据/列表的首选方式,如果它的迭代/循环速度比其他类型慢得多。

Jon*_*asH 6

首先,您的基准不够准确,无法得出任何结论。至少你应该使用秒表,我强烈建议使用benchmark.Net,因为这可以弥补基准测试时许多容易犯的错误。

其次,您应该很少(如果有的话)使用.ElementAt. IEnumerable当您需要迭代所有项目但不需要索引时,可以使用 an 。通常通过使用-loop,但如果确实需要,foreach您也可以获取枚举器并执行 -loop 。while(enumerator.MoveNext() {enumerator.Current}如果您确实需要索引,请使用IReadOnlyList

但即便如此,对数组或列表进行 for 循环也会比对可枚举对象进行 foreach 循环更快。这是因为 IEnumerable 更加抽象,并且这种抽象会带来一些性能成本。抽象是有代价的这种想法出现在编程的许多情况下。理想情况下,编译器能够优化掉这种成本,但这并不总是可能的。

IEnumerable实际上,在绝大多数情况下,列表/数组和列表/数组之间的性能差异是无关紧要的。计算机速度很快,而且大多数列表都相当小。如果您确实有巨大的数组,例如具有数百万像素的图像,那么您可能应该尝试坚持使用较低级别的构造,例如数组、Span<T>指针、for 循环等。