一次性返回所有可枚举的yield return; 不循环
我有以下函数来获取卡的验证错误。 我的问题涉及处理 GetErrors。 两种方法都具有相同的返回类型 IEnumerable
。
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
中的所有错误,而不必枚举它们?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
F# 支持整个集合的
yield!
和单个项目的yield
。 (这对于尾递归而言非常有用...)不幸的是,C# 不支持它。
但是,如果您有多个方法,每个方法都返回
IEnumerable
,则可以使用Enumerable.Concat
使代码更简单:两种实现之间有一个非常重要的区别不过:这个方法将立即调用所有方法,即使它一次只使用返回的迭代器。 您现有的代码将等到它循环遍历完
GetMoreErrors()
中的所有内容后,才会询问下一个错误。通常这并不重要,但值得了解何时会发生什么。
It is something that F# supports with
yield!
for a whole collection vsyield
for a single item. (That can be very useful in terms of tail recursion...)Unfortunately it's not supported in C#.
However, if you have several methods each returning an
IEnumerable<ErrorInfo>
, you can useEnumerable.Concat
to make your code simpler:There's one very important difference between the two implementations though: this one will call all of the methods immediately, even though it will only use the returned iterators one at a time. Your existing code will wait until it's looped through everything in
GetMoreErrors()
before it even asks about the next errors.Usually this isn't important, but it's worth understanding what's going to happen when.
您可以像这样设置所有错误源(方法名称借用 Jon Skeet 的答案)。
然后您可以同时迭代它们。
或者,您可以使用
SelectMany
展平错误源。GetErrorSources
中方法的执行也会被延迟。You could set up all the error sources like this (method names borrowed from Jon Skeet's answer).
You can then iterate over them at the same time.
Alternatively you could flatten the error sources with
SelectMany
.The execution of the methods in
GetErrorSources
will be delayed too.是的,可以一次返回所有错误。 只需返回
List
或ReadOnlyCollection
。通过返回
IEnumerable
,您将返回一个序列。 从表面上看,这似乎与返回集合相同,但您应该记住有许多区别。集合
序列
IEnumerable
允许延迟求值,而返回List
则不允许)。Yes it is possible to return all errors at once. Just return a
List<T>
orReadOnlyCollection<T>
.By returning an
IEnumerable<T>
you're returning a sequence of something. On the surface that may seem identical to returning the collection, but there are a number of difference, you should keep in mind.Collections
Sequences
IEnumerable<T>
allows for lazy evaluation, returningList<T>
does not).我很惊讶没有人想到在 IEnumerable> 上推荐一种简单的扩展方法来使此代码保持延迟执行。 我喜欢延迟执行的原因有很多,其中之一是即使对于巨大的可枚举量,内存占用也很小。
您可以在您的情况下使用它,就像这样
,您可以取消
DoGetErrors
周围的包装函数,只需将UnWrap
移动到调用站点。I'm surprised no one has thought to recommend a simple Extension method on
IEnumerable<IEnumerable<T>>
to make this code keep its deferred execution. I'm a fan of deferred execution for many reasons, one of them is that the memory footprint is small even for huge-mongous enumerables.And you could use it in your case like this
Similarly, you can do away with the wrapper function around
DoGetErrors
and just moveUnWrap
to the callsite.我没有看到你的功能有什么问题,我想说它正在做你想要的事情。
将 Yield 视为每次调用它时都会在最终枚举中返回一个元素,因此当您将它放在 foreach 循环中时,每次调用它时都会返回 1 个元素。 您可以在 foreach 中添加条件语句来过滤结果集。 (简单地通过不满足您的排除标准)
如果您稍后在方法中添加后续收益,它将继续向枚举添加 1 个元素,从而可以执行以下操作:
I don't see anything wrong with your function, I'd say that it is doing what you want.
Think of the Yield as returning an element in the final Enumeration each time that it is invoked, so when you have it in the foreach loop like that, each time it is invoked it returns 1 element. You have the ability to put conditional statements in your foreach to filter the resultset. (simply by not yielding on your exclusion criteria)
If you add subsequent yields later in the method, it will continue to add 1 element to the enumeration, making it possible to do things like...
我想出了一个快速的
yield_
片段:这是片段 XML:
I came up with a quick
yield_
snippet:Here's the snippet XML: