我有以下函数来获取验证错误的卡。 我的问题涉及到处理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_
片段:
![](https://www.manongdao.com/static/images/pcload.jpg)
下面是段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