假设我们有课
public class RMenuItem
{
public List<RMenuItem> ChildrenItems { get; }
public decimal OperationID { get; }
public string Name { get; }
}
正如您所看到的 - 每个菜单项都可以包含子项 - 与菜单中一样。 我的任务是遍历此列表中的每个项目并对其应用一些操作。经典决策是编写递归迭代。但是,如果LINQ可以让我的任务变得更容易,我会感兴趣吗?例如,我想我们可以编写可以得到平面对象列表的查询,我可以使用foreach简单地迭代。但我这样做的尝试还没有成功。 所以任何帮助表示赞赏!
答案 0 :(得分:4)
可能:
public void PrintAllNames(RMenuItem rootItem)
{
Action<RMenuItem> print = null;
print = m =>
{
Console.WriteLine(m.Name);
m.ChildrenItems.ForEach(print);
};
print(rootItem);
}
请注意如何声明print
以便print
可以自行使用。这与递归方法直接相当,我宁愿使用:
public void PrintAllNames(RMenuItem rootItem)
{
Console.WriteLine(rootItem.Name);
rootItem.ChildrenItems.ForEach(PrintAllNames);
}
(虽然对于更复杂的情况,可能功能解决方案最有意义)
答案 1 :(得分:1)
事实上,您可以使用LINQ,SelectMany
列出列表,只是一些示例
menuItemsList.SelectMany(x => x.ChildrenItems).Where(c => c.someChildProperty);
由于
修改强>
在回应评论时,我刚才给出了SelectMany
的例子。谢谢你指出。
menuItemsList.SelectMany(x => x.ChildrenItems.Select(p => p)).Where(c => c.someChildProperty);
或类似的东西
menuItemsList.SelectMany(x => x.ChildrenItems).Select(p => p).Where(c => c.someChildProperty);
<强> EDIT2 强>
啊......现在我明白了你想要的......
我们可以稍微修改我的上述查询以执行您想要的操作
menuItemsList
.SelectMany(x => { //do something with x like printing it
x.ChildrenItems
})
.Select(p => { // do something with p like printing it
p
});
基本上你可以在{}
由于
答案 2 :(得分:1)
我建议实现这两种方法。您可以使用实用程序方法来获取所有项目,或者可以实现Visitor Pattern,但这意味着更改RMenuItem
类。
效用方法:
static IEnumerable<RMenuItem> GetAllMenuItems(IList<RMenuItem> items)
{
if (items == null)
throw new ArgumentNullException("items");
Queue<RMenuItem> queue = new Queue<RMenuItem>(items);
while (queue.Count > 0)
{
var item = queue.Dequeue();
if (item.ChildrenItems != null)
{
foreach (var child in item.ChildrenItems)
{
queue.Enqueue(child);
}
}
yield return item;
}
}
我更喜欢递归的命令式方法,因为我们可以使用迭代器块。
访客模式:
public interface IRMenuItemVisitor
{
void Visit(RMenuItem item);
}
public class PrintRMenuItemVisitor : IRMenuItemVisitor
{
public void Visit(RMenuItem item)
{
Console.WriteLine(item);
}
}
public interface IRMenuItem
{
void Accept(IRMenuItemVisitor visitor);
}
public class RMenuItem : IRMenuItem
{
// ...
public void Accept(IRMenuItemVisitor visitor)
{
visitor.Visit(this);
if (ChildrenItems != null)
{
foreach (var item in ChildrenItems)
{
item.Accept(visitor);
}
}
}
}
用法:
RMenuItem m1 = new RMenuItem
{
Name = "M1",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M11" },
new RMenuItem {
Name = "M12",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M121" },
new RMenuItem { Name = "M122" }
}
}
}
};
RMenuItem m2 = new RMenuItem
{
Name = "M2",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M21" },
new RMenuItem { Name = "M22" },
new RMenuItem { Name = "M23" }
}
};
IList<RMenuItem> menus = new List<RMenuItem> { m1, m2 };
foreach (var menu in GetAllMenuItems(menus))
{
Console.WriteLine(menu);
}
// or
IList<RMenuItem> menus = new List<RMenuItem> { m1, m2 };
foreach (var menu in menus)
{
menu.Accept(new PrintRMenuItemVisitor());
}
答案 3 :(得分:1)
您可以在类中定义Flatten方法(如果您愿意,也可以作为扩展名),如此
public IEnumerable<RMenuItem> Flatten()
{
foreach (var item in ChildrenItems)
{
yield return item;
}
return ChildrenItems.SelectMany(item => item.Flatten());
}
然后对每个元素做一些事情就像
一样简单RMenuItem rootItem ;
// do somthing with the root item
foreach (var item in rootItem.Flatten())
{
// do somthing
}