IEnumerable和递归使用yield return
我有一个IEnumerable<T>
方法用于在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<Object>
,则会生成代码,但输出中会返回错误的类型。
有没有一种使用yield return
同时也使用递归的方法?
在返回IEnumerable<T>
的方法内部, yield return
必须返回T
,而不是IEnumerable<T>
。
更换
yield return c.GetDeepControlsByType<T>();
有:
foreach (var x in c.GetDeepControlsByType<T>())
{
yield return x;
}
您需要产生递归调用产生的每个项目:
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)
{
foreach (Control control in c.GetDeepControlsByType<T>())
{
yield return control;
}
}
}
}
请注意,以这种方式递归会产生成本 - 最终会创建大量迭代器,如果您拥有非常深的控制树,则可能会产生性能问题。 如果你想避免这种情况,你基本上需要在方法中自己完成递归,以确保只有一个迭代器(状态机)被创建。 看到这个问题的更多细节和示例实现 - 但这显然增加了一定的复杂性。
Jon Skeet和Panic上校在答复中指出,如果树很深,在递归方法中使用yield return
可能会导致性能问题。
下面是一个通用的非递归扩展方法,它执行一系列树的深度优先遍历:
public static IEnumerable<TSource> RecursiveSelect<TSource>(
this IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>> childSelector)
{
var stack = new Stack<IEnumerator<TSource>>();
var enumerator = source.GetEnumerator();
try
{
while (true)
{
if (enumerator.MoveNext())
{
TSource element = enumerator.Current;
yield return element;
stack.Push(enumerator);
enumerator = childSelector(element).GetEnumerator();
}
else if (stack.Count > 0)
{
enumerator.Dispose();
enumerator = stack.Pop();
}
else
{
yield break;
}
}
}
finally
{
enumerator.Dispose();
while (stack.Count > 0) // Clean up in case of an exception.
{
enumerator = stack.Pop();
enumerator.Dispose();
}
}
}
与Eric Lippert的解决方案不同,RecursiveSelect直接与枚举器一起工作,因此它不需要调用Reverse(缓冲整个内存序列)。
使用RecursiveSelect,OP的原始方法可以像这样重写:
public static IEnumerable<Control> GetDeepControlsByType<T>(this Control control)
{
return control.Controls.RecursiveSelect(c => c.Controls).Where(c => c is T);
}
链接地址: http://www.djcxy.com/p/53709.html