为什么不能在同一方法中使用“返回”和“收益率”?

时间:2012-03-09 08:51:18

标签: c# return ienumerable yield-return

为什么我们不能在同一方法中同时使用return和yield返回?

例如,我们可以在下面使用GetIntegers1和GetIntegers2,但不能使用GetIntegers3。

public IEnumerable<int> GetIntegers1()
{
  return new[] { 4, 5, 6 };
}

public IEnumerable<int> GetIntegers2()
{
  yield return 1;
  yield return 2;
  yield return 3;
}

public IEnumerable<int> GetIntegers3()
{
  if ( someCondition )
  {
    return new[] {4, 5, 6}; // compiler error
  }
  else
  {
    yield return 1;
    yield return 2;
    yield return 3;
  }
}

5 个答案:

答案 0 :(得分:17)

return非常渴望。它立即返回整个结果集。 yield return构建一个枚举器。在幕后,当您使用yield return时,C#编译器会为枚举器发出必要的类。在确定是否应该发出可枚举的代码或者具有返回简单数组的方法时,编译器不会查找if ( someCondition )等运行时条件。它检测到你的方法中你正在使用两者,这是不可能的,因为他不能发出枚举器的代码,同时让方法返回一个普通的数组,所有这些都用于相同的方法。

答案 1 :(得分:11)

不,你不能这样做 - 迭代器块(具有yield的东西不能使用常规(非收益)return。相反,您需要使用2种方法:

public IEnumerable<int> GetIntegers3()
{
  if ( someCondition )
  {
    return new[] {4, 5, 6}; // compiler error
  }
  else
  {
    return GetIntegers3Deferred();
  }
}
private IEnumerable<int> GetIntegers3Deferred()
{
    yield return 1;
    yield return 2;
    yield return 3;
}

或者因为在这种特定情况下,其他两种方法中已存在两者的代码:

public IEnumerable<int> GetIntegers3()
{
  return ( someCondition ) ? GetIntegers1() : GetIntegers2();
}

答案 2 :(得分:8)

编译器使用yield语句(return或break)重写任何方法。它目前无法处理可能会或可能不会yield的方法。

我建议您阅读Jon Skeet C# in Depth的第6章,其中第6章是免费提供的 - 它非常好地涵盖了迭代器块。

我认为没有理由为什么在c#编译器的未来版本中这是不可能的。其他.Net语言确实以'yield from'运算符(See F# yield!)的形式支持类似的东西。如果这样的运算符存在于c#中,它将允许您以以下形式编写代码:

public IEnumerable<int> GetIntegers()
{
  if ( someCondition )
  {
    yield! return new[] {4, 5, 6};
  }
  else
  {
    yield return 1;
    yield return 2;
    yield return 3;
  }
}

答案 3 :(得分:3)

从理论上讲,我认为没有理由不能将return和yield return混合在一起:编译器首先将任何return (blabla());句子语法转换为:

是一件容易的事。
var myEnumerable = blabla();
foreach (var m in myEnumerable) 
    yield return m; 
yield break; 

然后继续(将整个方法转换为......现在转换它们的内容;内部匿名IEnumerator类?!)

那么他们为什么不选择实施呢,这里有两个猜测:

  • 他们可能已经决定让用户同时收到退货和收益率会让人感到困惑,

  • 返回整个可枚举的更快,更便宜但也渴望;通过yield return建立起来有点贵(特别是如果递归调用,请参阅Eric Lippert在二进制树中遍历带有yield return语句的警告:https://stackoverflow.com/a/3970171/671084 例如)但懒惰。因此,用户通常不希望混合这些:如果您不需要懒惰(即您知道整个序列)不会受到效率损失,只需使用常规方法。他们可能想强迫用户按照这些思路进行思考。

另一方面,似乎有些情况下用户可以从一些语法扩展中受益;你可能想要阅读这个问题和答案作为一个例子(不是同一个问题,但可能有一个类似的动机):Yield Return Many?

答案 4 :(得分:0)

我认为它不起作用的主要原因是因为设计方式不是过于复杂但同时具有高效性,但效果相对较小。

您的代码到底会做什么?它会直接返回数组,还是会迭代它?

如果它会直接返回数组,那么你必须在return允许的条件下考虑复杂的规则,因为return之后的yield return没有意义。你可能需要生成复杂的代码来决定该方法是返回自定义迭代器还是数组。

如果你想迭代这个集合,你可能想要一些更好的关键字。 Something like yield foreach。这实际上是考虑过,但最终没有实施。我想我记得读到的主要原因是,如果你有几个嵌套的迭代器,它实际上很难让它表现得很好。