IEnumerable<> vs List<> 作为参数

Mur*_*ock 21 c# performance ienumerable benchmarking

一般来说IEnumerable<>,当我传入参数时,我倾向于使用作为类型。然而,根据 BenchmarkDotNet:

[Benchmark]
public void EnumeratingCollectionsBad()
{
    var list = new List<string>();
    for (int i = 0; i < 1000; i++)
    {
        Bad(list);
    }
}

[Benchmark]
public void EnumeratingCollectionsFixed()
{
    var list = new List<string>();
    for (int i = 0; i < 1000; i++)
    {
        Fixed(list);
    }
}

private static void Bad(IEnumerable<string> list)
{
    foreach (var item in list)
    {
    }
}

private static void Fixed(List<string> list)
{
    foreach (var item in list)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)
方法 工作 运行 意思 错误 标准差 中位数 第 0 代 第一代 第 2 代 已分配
枚举集合坏 .NET 核心 3.1 .NET 核心 3.1 17.802 美元 0.3670 美元 1.0764 美元 17.338 美元 6.3782 —— —— 40032乙
枚举集合固定 .NET 核心 3.1 .NET 核心 3.1 5.015 美元 0.1003 美元 0.2535 美元 4.860 美元 —— —— —— 32 乙

为什么接口版本会比具体版本慢得多(和内存密集型)?

Jon*_*eet 34

为什么接口版本会比具体版本慢得多(和内存密集型)?

当它使用接口时,迭代必须在堆上分配一个对象......而List<T>.GetEnumerator()返回 a List<T>.Enumerator,它是一个结构,不需要任何额外的分配。List<T>.Enumeratorimplements IEnumerator<T>,但因为编译器直接知道具体类型,所以不需要装箱。

因此,即使两种方法都在同一类型 (a List<T>)的对象上运行,但仍会调用此方法:

IEnumerator<T> GetEnumerator()
Run Code Online (Sandbox Code Playgroud)

......有人称之为:

List<T>.Enumerator GetEnumerator()
Run Code Online (Sandbox Code Playgroud)

第一个几乎可以肯定只是委托给第二个,但必须将结果装箱,因为它IEnumerator<T>是一个引用类型。

List<T>.GetEnumerator()返回可变结构的事实可能会产生一些令人惊讶的后果,但它的设计正是为了获得您在此处看到的性能优势。

VS具体类型的使用界面本身有一些很轻微的性能损失,但这里的主要原因是分配的差异。

  • 我在[博客文章](https://codeblog.jonskeet.uk/2010/07/27/iterate-damn-you/“迭代,该死的你!”)中的测验失败了。:-) (2认同)