如果我想在 ASP.NET 页面上找到选中的复选框,我可以使用以下 LINQ 查询。
var checkBoxes = this.Controls
.OfType<CheckBox>()
.TakeWhile<CheckBox>(cb => cb.Checked);
如果复选框嵌套在当前控件集合中,则效果很好,但我想知道如何通过向下钻取到顶级控件的控件集合来扩展搜索。
问题在这里被问到:
并收到非 LINQ 的答案,我已经有了自己的类型和 ID 递归控制搜索版本作为扩展方法,但我只是想知道这在 LINQ 中有多容易?
从递归中取出类型/ID 检查,所以只需要一个“递归地给我所有控件”的方法,例如
public static IEnumerable<Control> GetAllControls(this Control parent)
{
foreach (Control control in parent.Controls)
{
yield return control;
foreach(Control descendant in control.GetAllControls())
{
yield return descendant;
}
}
}
这有点低效(就创建大量迭代器而言),但我怀疑你会有一棵很深的树。
然后,您可以将原始查询编写为:
var checkBoxes = this.GetAllControls()
.OfType<CheckBox>()
.TakeWhile<CheckBox>(cb => cb.Checked);
(编辑:将 AllControls 更改为 GetAllControls 并将其正确用作方法。)
public static IEnumerable<Control> AllControls(this Control container)
{
//Get all controls
var controls = container.Controls.Cast<Control>();
//Get all children
var children = controls.Select(c => c.AllControls());
//combine controls and children
var firstGen = controls.Concat(children.SelectMany(b => b));
return firstGen;
}
现在基于上面的函数,我们可以这样做:
public static Control FindControl(this Control container, string Id)
{
var child = container.AllControls().FirstOrDefault(c => c.ID == Id);
return child;
}
我对AllControls
递归的建议是:
public static IEnumerable<Control> AllControls(this Control parent)
{
foreach (Control control in parent.Controls)
{
yield return control;
}
foreach (Control control in parent.Controls)
{
foreach (Control cc in AllControls(control)) yield return cc;
}
}
第二个foreach
看起来很奇怪,但这是我所知道的“扁平化”递归调用的唯一方法。
这行得通,但是如果您像我们在我当前的工作场所中那样使用第三方控件和您自己的服务器控件,您可以获得很多级别的控件嵌套。我想 ASP.NET 页面只是更普遍问题的一个示例,即在 Linq 中深入了解嵌套集合。
我以前只使用过这种技术。(如果你想变得非常甜蜜,你可以用 Y Combinator 将其作为一个表达式来完成!)顺便说一句,AllControls 不应该用作一种方法吗?
@Phil:是的,它可能会下降几个级别并且效率有点低 - 但你有任何理由相信它会成为一个重要的瓶颈吗?先做最简单有效的事情,如果真的有问题再优化。(例如,单个数据库调用可能会压倒此成本。)
@Jon,多年来我已经通过我问题中链接中的递归搜索解决了这个问题。只是想让我的一天更有趣地重新发明轮子,毕竟今天是星期五 :) 无论如何谢谢。
类似于新问题:stackoverflow.com/questions/3419159 /... - 我很想知道使用DescendantsLINQ 函数进行递归控制选择的正确方法。(也许还更新这个问题/答案?)