【问题标题】:What is better when using an IEnumerable with one item: yield return or return []?将 IEnumerable 与一项一起使用时哪个更好:yield return 或 return []?
【发布时间】:2012-01-03 08:35:47
【问题描述】:

这是“你可以通过多种方式做到这一点”的问题之一。考虑以下代码:

protected virtual IEnumerable<ScriptReference> GetScriptReferences()
{
    ScriptReference referece = new ScriptReference();
    referece.Assembly = "FeyenoordEnabled";
    referece.Name = "FeyenoordEnabled.PassTextBox.js";

    return new ScriptReference[] { referece }; 
}

protected virtual IEnumerable<ScriptReference> GetScriptReferences()
{
    ScriptReference referece = new ScriptReference();
    referece.Assembly = "FeyenoordEnabled";
    referece.Name = "FeyenoordEnabled.PassTextBox.js";

    yield return referece;
}

我只需要退回 一个 项。第一段代码返回一个包含单个项目的数组,第二个代码产生该项目。 什么更好,为什么?

【问题讨论】:

  • 肯定不可能比第一个return更有效率吗?
  • 你也可以return Enumerable.Repeat(reference, 1); 虽然这归结为yield return 在封面下......
  • 我会注意到yield 的另一个用途:创建一个生成的、无休止的 IEnumerable(例如,斐波那契序列生成器)。如果您要这样做,您必须小心不要尝试循环遍历整个 IEnumerable。但是,有趣的是,我可以告诉您,出于性能原因,我会避免使用yield return。似乎其他人正在确认这种做法。

标签: c# .net ienumerable yield


【解决方案1】:

yield 是一个非常昂贵的关键字。你告诉编译器做很多事情。如果性能不是问题,请使用更优雅的代码。但是,如果性能是一个问题,请坚持使用阵列。

根据过去的经验,我可以说,摆脱这种yield 使用方式让我获得了一些可观的性能提升。但一如既往,剖析并找出真正的瓶颈。

【讨论】:

  • 如果yield 这么贵,什么时候才“合适”呢?
  • Linq 基于 yield 关键字的使用。 yield 使您的函数变得懒惰,如果您不获取元素,则什么也不会发生。你发的case,直接返回有道理,不用yield。
【解决方案2】:

配置文件配置文件配置文件。这是使用单声道的 A-B 比较:

public static IEnumerable<int> UsingYield()
{
    yield return 42;
}
public static IEnumerable<int> ReturningArray()
{
    return new []{ 42 };
}

(编译时启用-optimize+

yield 版本实例化一个实现IEnumerable 和整个shebang 的类:

注意 我省略了实现枚举器块“匿名”类型Program/'&lt;UsingYield&gt;c__Iterator0' 的 163 行 CIL 代码。在这里查看全部内容:https://gist.github.com/1384014

.method public static hidebysig 
       default class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> UsingYield ()  cil managed 
{
    .custom instance void class [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::'.ctor'() =  (01 00 00 00 ) // ....

    // Method begins at RVA 0x20f4
// Code size 16 (0x10)
.maxstack 3
.locals init (
    class Program/'<UsingYield>c__Iterator0'    V_0)
IL_0000:  newobj instance void class Program/'<UsingYield>c__Iterator0'::'.ctor'()
IL_0005:  stloc.0 
IL_0006:  ldloc.0 
IL_0007:  dup 
IL_0008:  ldc.i4.s 0xfffffffe
IL_000a:  stfld int32 Program/'<UsingYield>c__Iterator0'::$PC
IL_000f:  ret 
} // end of method Program::UsingYield

数组版本似乎简单多了:

.method public static hidebysig 
       default class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> ReturningArray ()  cil managed 
{
    // Method begins at RVA 0x2110
// Code size 12 (0xc)
.maxstack 8
IL_0000:  ldc.i4.1 
IL_0001:  newarr [mscorlib]System.Int32
IL_0006:  dup 
IL_0007:  ldc.i4.0 
IL_0008:  ldc.i4.s 0x2a
IL_000a:  stelem.i4 
IL_000b:  ret 
} // end of method Program::ReturningArray

关于实际运行时性能,PROFILE PROFILE PROFILE!

【讨论】:

  • 如果yield 这么贵,什么时候才“合适”呢?
  • @KeesC.Bakker:就生成的代码而言,它是复杂的。您将出于其他地方提到的原因使用它:(a) 延迟执行 (b) 抽象(您获得状态机功能的代价是程序员看不到复杂性)。此外,没有人说 JIT 引擎不会内联相关方法并获得接近相同的结果。个人资料,个人资料,个人资料!
  • 好的,先生。先知……“从今天开始,我要出去做人!”
  • 对于那些不想分析的人来说,C# 7/.Net 4.7 有轻微的速度优势来产生回报。
【解决方案3】:

当你用你创建的数组调用它时,第一个直接返回。

第二个,因为您使用的是 yield,它甚至不会执行,直到您开始获取元素(在您的情况下是一个元素)。

所以这真的取决于你想做什么,但要注意不同的行为。

【讨论】:

    【解决方案4】:

    使用 Benchmark.NET 进行分析后,很明显使用数组作为单个值比使用 yield return 更快。

    使用 .NET Core 3.1.2 x64 Windows 测试

    【讨论】:

      猜你喜欢
      • 2011-01-04
      • 2010-11-07
      • 1970-01-01
      • 2011-12-09
      • 1970-01-01
      • 2019-01-01
      • 2011-04-20
      • 2016-10-04
      • 1970-01-01
      相关资源
      最近更新 更多