使用yield return 的IEnumerable 和递归
我有一个 IEnumerable
方法,用于在 WebForms 页面中查找控件。
该方法是递归的,当yield return
返回递归调用的值时,我在返回我想要的类型时遇到了一些问题。
我的代码如下所示:
public static IEnumerable<Control> GetDeepControlsByType<T>(this Control control)
{
foreach(Control c in control.Controls)
{
if (c is T)
{
yield return c;
}
if(c.Controls.Count > 0)
{
yield return c.GetDeepControlsByType<T>();
}
}
}
当前会引发“无法转换表达式类型”错误。但是,如果此方法返回类型 IEnumerable
有没有一种方法可以在使用yield return
的同时也使用递归?
I have an IEnumerable<T>
method that I'm using to find controls in a WebForms page.
The method is recursive and I'm having some problems returning the type I want when the yield return
is returnig the value of the recursive call.
My code looks as follows:
public static IEnumerable<Control> GetDeepControlsByType<T>(this Control control)
{
foreach(Control c in control.Controls)
{
if (c is T)
{
yield return c;
}
if(c.Controls.Count > 0)
{
yield return c.GetDeepControlsByType<T>();
}
}
}
This currently throws a "Cannot convert expression type" error. If however this method returns type IEnumerable<Object>
, the code builds, but the wrong type is returned in the output.
Is there a way of using yield return
whilst also using recursion?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
在返回
IEnumerable
的方法中,yield return
必须返回T
,而不是IEnumerable
>。替换
为:
Inside a method that returns
IEnumerable<T>
,yield return
has to returnT
, not anIEnumerable<T>
.Replace
with:
您需要生成递归调用生成的每一项:
请注意,以这种方式递归是有成本的 - 您最终将创建大量迭代器,如果出现以下情况,这可能会产生性能问题:你有一个非常深的控制树。如果你想避免这种情况,你基本上需要在方法中自己进行递归,以确保只创建一个迭代器(状态机)。有关更多详细信息和示例实现,请参阅此问题 - 但这显然也增加了一定程度的复杂性。
You need to yield each of the items yielded by the recursive call:
Note that there's a cost to recursing in this way - you'll end up creating a lot of iterators, which can create a performance issue if you have a really deep control tree. If you want to avoid that, you basically need to do the recursion yourself within the method, to make sure there's only one iterator (state machine) created. See this question for more details and a sample implementation - but this obviously adds a certain amount of complexity too.
正如 Jon Skeet 和 Colonel Panic 在他们的回答中指出的那样,如果树很深,在递归方法中使用
yield return
可能会导致性能问题。这是一个通用的非递归扩展方法,它执行树序列的深度优先遍历:
与 Eric 不同Lippert 的解决方案,RecursiveSelect 直接与枚举器一起工作,因此不需要调用 Reverse(它将整个序列缓冲在内存中)。
使用RecursiveSelect,OP的原始方法可以简单地重写如下:
As Jon Skeet and Colonel Panic note in their answers, using
yield return
in recursive methods may cause performance problems if the tree is very deep.Here's a generic non-recursive extension method that performs a depth-first traversal of a sequence of trees:
Unlike Eric Lippert's solution, RecursiveSelect works directly with enumerators so that it doesn't need to call Reverse (which buffers the entire sequence in memory).
Using RecursiveSelect, the OP's original method can be rewritten simply like this:
其他人为您提供了正确的答案,但我认为您的案例不会因屈服而受益。
这是一个在不产生任何结果的情况下实现相同目的的代码片段。
Others provided you with the correct answer, but I don't think your case benefits from yielding.
Here's a snippet which achieves the same without yielding.
您需要在第二个
yield return
中从枚举器返回项目,而不是枚举器本身You need to return the items from the enumerator, not the enumerator itself, in your second
yield return
Seredynski 的语法 是正确的,但您应该小心避免在递归函数中使用
yield return
因为它内存使用的灾难。请参阅 https://stackoverflow.com/a/3970171/284795 它会随着深度的增加而爆炸性地扩展(类似的函数使用 10我的应用程序中的内存百分比)。一种简单的解决方案是使用一个列表并通过递归传递它 https://codereview.stackexchange.com/a/5651/ 754章
第 /a/5661/754
Seredynski's syntax is correct, but you should be careful to avoid
yield return
in recursive functions because it's a disaster for memory usage. See https://stackoverflow.com/a/3970171/284795 it scales explosively with depth (a similar function was using 10% of memory in my app).A simple solution is to use one list and pass it with the recursion https://codereview.stackexchange.com/a/5651/754
Alternatively you could use a stack and a while loop to eliminate recursive calls https://codereview.stackexchange.com/a/5661/754
我认为你必须返回枚举中的每个控件。
I think you have to yield return each of the controls in the enumerables.
虽然有很多好的答案,但我仍然想补充一点,可以使用 LINQ 方法来完成同样的事情,.
例如,OP的原始代码可以重写为:
While there are many good answers out there, I would still add that it is possible to use LINQ methods to accomplish the same thing, .
For instance, the original code of the OP could be rewritten as: