嵌套产量与回报的IEnumerable(Nested yield return with IEnum

2019-06-18 10:17发布

我有以下函数来获取验证错误的卡。 我的问题涉及到处理GetErrors。 这两种方法都具有相同的返回类型IEnumerable<ErrorInfo>

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
    var errors = GetMoreErrors(card);
    foreach (var e in errors)
        yield return e;

    // further yield returns for more validation errors
}

是否有可能返回的所有错误GetMoreErrors而不必通过它们来枚举?

关于它的这个思路可能是一个愚蠢的问题,但我想确保我不会出错。

Answer 1:

这绝对不是一个愚蠢的问题,和它的东西,F#与支持yield! 整整一个集合VS yield为单个项目。 (也可在尾递归方面非常有用...)

可惜这不是在C#中的支持。

但是,如果你有各自返回一个多种方法IEnumerable<ErrorInfo>您可以用Enumerable.Concat使你的代码更简单:

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
    return GetMoreErrors(card).Concat(GetOtherErrors())
                              .Concat(GetValidationErrors())
                              .Concat(AnyMoreErrors())
                              .Concat(ICantBelieveHowManyErrorsYouHave());
}

有两种实现之间有一个很重要的区别,但:此人会立即调用所有的方法,即使它只能使用返回的迭代器一次一个。 您现有的代码将等待,直到它通过一切环状GetMoreErrors()它甚至询问下一个错误之前。

通常这并不重要,但它是值得理解这是怎么回事的时候发生。



Answer 2:

你可以建立这样的所有误差源(方法名来自乔恩斯基特的答案借来的)。

private static IEnumerable<IEnumerable<ErrorInfo>> GetErrorSources(Card card)
{
    yield return GetMoreErrors(card);
    yield return GetOtherErrors();
    yield return GetValidationErrors();
    yield return AnyMoreErrors();
    yield return ICantBelieveHowManyErrorsYouHave();
}

然后,您可以在同一时间对它们进行迭代。

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
    foreach (var errorSource in GetErrorSources(card))
        foreach (var error in errorSource)
            yield return error;
}

另外,您可以用压平误差源SelectMany

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
    return GetErrorSources(card).SelectMany(e => e);
}

在方法的执行GetErrorSources也将被推迟。



Answer 3:

我想出了一个快速yield_片段:

下面是段XML:

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Author>John Gietzen</Author>
      <Description>yield! expansion for C#</Description>
      <Shortcut>yield_</Shortcut>
      <Title>Yield All</Title>
      <SnippetTypes>
        <SnippetType>Expansion</SnippetType>
      </SnippetTypes>
    </Header>
    <Snippet>
      <Declarations>
        <Literal Editable="true">
          <Default>items</Default>
          <ID>items</ID>
        </Literal>
        <Literal Editable="true">
          <Default>i</Default>
          <ID>i</ID>
        </Literal>
      </Declarations>
      <Code Language="CSharp"><![CDATA[foreach (var $i$ in $items$) yield return $i$$end$;]]></Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>


Answer 4:

我看不出有什么毛病你的功能,我会说,它是做你想要什么。

产量看作最终计数每次调用时返回的元素,所以当你在foreach循环一样,拥有它,每次调用它时返回1元。 你必须把条件语句在你的foreach来筛选结果集的能力。 (简单地通过没有产生对你的排除标准)

如果您添加后续产量在后面的方法,它将继续1个元素添加到枚举,从而能够做这样的事情?

public IEnumerable<string> ConcatLists(params IEnumerable<string>[] lists)
{
  foreach (IEnumerable<string> list in lists)
  {
    foreach (string s in list)
    {
      yield return s;
    }
  }
}


Answer 5:

是的,它可以立刻返回的所有错误。 只返回一个List<T>ReadOnlyCollection<T>

通过返回IEnumerable<T>您传回的一些序列。 在这看似相同的返回集合,但也有一些差异的表面,你应该记住。

集合

  • 呼叫者可以肯定的是返回集合时,无论是收集所有的项目都会存在。 如果集合必须每次通话被创建,返回集合是一个非常糟糕的主意。
  • 大多数集合可以返回时被修改。
  • 集合是有限的大小。

序列

  • 可以列举 - 这是几乎所有我们可以肯定地说。
  • 返回的序列本身不能被修改。
  • 每个元件可被创建为通过顺序运行的一部分(即,返回IEnumerable<T>允许惰性计算,返回List<T>不)。
  • 序列可以是无限的,因此它留给调用者来决定有多少元素应该返回。


Answer 6:

我很惊讶,谁也没有想到推荐一个简单的扩展方法IEnumerable<IEnumerable<T>>使此代码保持它的延迟执行。 我有很多原因延迟执行的粉丝,其中之一是,内存占用小,甚至巨额-mongous可枚举。

public static class EnumearbleExtensions
{
    public static IEnumerable<T> UnWrap<T>(this IEnumerable<IEnumerable<T>> list)
    {
        foreach(var innerList in list)
        {
            foreach(T item in innerList)
            {
                yield return item;
            }
        }
    }
}

而你可以在你的情况下,像这样使用它

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
    return DoGetErrors(card).UnWrap();
}

private static IEnumerable<IEnumerable<ErrorInfo>> DoGetErrors(Card card)
{
    yield return GetMoreErrors(card);

    // further yield returns for more validation errors
}

同样的,你可以做掉周围的包装函数DoGetErrors和刚刚转会UnWrap到调用点。



文章来源: Nested yield return with IEnumerable