Joh*_*ley 158 c# ienumerable yield yield-return
我有以下功能来获取卡的验证错误.我的问题涉及处理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
}
Run Code Online (Sandbox Code Playgroud)
是否可以返回所有错误GetMoreErrors而无需通过它们进行枚举?
想一想这可能是一个愚蠢的问题,但我想确保我不会出错.
Jon*_*eet 136
这绝对不是一个愚蠢的问题,它是F#支持yield!整个集合与yield单个项目相关的东西.(这在尾递归方面非常有用......)
不幸的是,C#不支持它.
但是,如果您有多个方法返回IEnumerable<ErrorInfo>,则可以使用Enumerable.Concat更简单的代码:
private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
return GetMoreErrors(card).Concat(GetOtherErrors())
.Concat(GetValidationErrors())
.Concat(AnyMoreErrors())
.Concat(ICantBelieveHowManyErrorsYouHave());
}
Run Code Online (Sandbox Code Playgroud)
但是这两个实现之间有一个非常重要的区别:这个方法会立即调用所有方法,即使它一次只使用一个返回的迭代器.您现有的代码将等到它GetMoreErrors()之前的所有内容循环,甚至会询问下一个错误.
通常这并不重要,但值得了解什么时候会发生什么.
Ada*_*ton 23
您可以像这样设置所有错误源(从Jon Skeet的答案中借用的方法名称).
private static IEnumerable<IEnumerable<ErrorInfo>> GetErrorSources(Card card)
{
yield return GetMoreErrors(card);
yield return GetOtherErrors();
yield return GetValidationErrors();
yield return AnyMoreErrors();
yield return ICantBelieveHowManyErrorsYouHave();
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以同时迭代它们.
private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
foreach (var errorSource in GetErrorSources(card))
foreach (var error in errorSource)
yield return error;
}
Run Code Online (Sandbox Code Playgroud)
或者,您可以使用平坦化错误源SelectMany.
private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
return GetErrorSources(card).SelectMany(e => e);
}
Run Code Online (Sandbox Code Playgroud)
方法的执行GetErrorSources也会延迟.
Joh*_*zen 15
我想出了一个快速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>
Run Code Online (Sandbox Code Playgroud)
我没有看到你的功能有什么问题,我会说它正在做你想要的.
将Yield视为在每次调用它时在最终枚举中返回一个元素,所以当你在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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
我很惊讶没有人想到推荐一种简单的扩展方法IEnumerable<IEnumerable<T>>来使此代码保持延迟执行。我喜欢延迟执行的原因有很多,其中之一是即使对于庞大的可枚举项,内存占用也很小。
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;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
你可以像这样在你的情况下使用它
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
}
Run Code Online (Sandbox Code Playgroud)
类似地,您可以取消周围的包装函数,DoGetErrors而只需移至UnWrap调用站点。
| 归档时间: |
|
| 查看次数: |
33494 次 |
| 最近记录: |