为什么“回报”和“收益回报”不能用同一种方法

本文关键字:回报 方法 一种 不能 收益 收益回报 为什么 | 更新日期: 2023-09-27 18:27:18

为什么我们不能在同一方法中同时使用回报和收益回报?

例如,我们可以在下面使用 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;
  }
}

为什么“回报”和“收益回报”不能用同一种方法

return很急切。它一次返回整个结果集。 yield return生成一个枚举器。在后台,当您使用 yield return 时,C# 编译器会发出枚举器所需的类。编译器在确定是否应发出可枚举的代码或具有返回简单数组的方法时,不会查找运行时条件(如if ( someCondition )(。它检测到在您的方法中您同时使用两者,这是不可能的,因为他无法发出枚举器的代码,同时让该方法返回一个普通数组,所有这些都用于同一方法。

不,你不能这样做 - 迭代器块(带有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;
}

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

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

使用 yield 语句(返回或中断(重写任何方法。它目前无法处理可能yield也可能不会的方法。

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

但是,我认为没有理由在c#编译器的未来版本中无法做到这一点。其他 .Net 语言确实支持类似的东西,以"yield from"运算符的形式(见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;
  }
}

从理论上讲,我认为没有理由不能将收益和收益回报混为一谈: 编译器很容易首先将任何return (blabla());句子语法转换为:

var myEnumerable = blabla();
foreach (var m in myEnumerable) 
    yield return m; 
yield break; 

然后继续(将整个方法转换为...无论他们现在如何改变它;一个内部匿名IEnumerator类?!

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

  • 他们可能已经决定同时获得回报和收益回报会让用户感到困惑,

  • 返回整个枚举更快、更便宜,但也更急切;通过收益回报进行构建的成本要高一些(特别是如果递归调用,请参阅 Eric Lippert 关于使用收益返回语句在二叉树中进行遍历的警告: https://stackoverflow.com/a/3970171/671084例如(但懒惰。因此,用户通常不想混合这些:如果您不需要懒惰(即您知道整个序列(,请不要遭受效率损失,只需使用普通方法即可。他们可能想强迫用户沿着这些思路思考。

另一方面,似乎确实存在用户可以从某些语法扩展中受益的情况;您可能希望将此问题和答案作为示例阅读(不是相同的问题,但可能具有相似的动机(:收益回报多?

我认为它不起作用的主要原因是因为以不太复杂但同时具有性能的方式设计它会很困难,而且收益相对较小。

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

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

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