3

假设我们有课

public class RMenuItem
{
    public List<RMenuItem> ChildrenItems { get; }
    public decimal OperationID { get; }
    public string Name { get; }
}

如您所见 - 每个菜单项都可以有子项 - 像往常一样在菜单中。我的任务是遍历此列表的每个项目并对其应用一些操作。经典决策是编写递归迭代。但如果 LINQ 可以让我的任务更轻松,我会很感兴趣吗?例如,我假设我们可以编写查询来获取对象的平面列表,我可以简单地使用 foreach 对其进行迭代。但是我这种方式的尝试还没有成功。所以任何帮助表示赞赏!

4

4 回答 4

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);
    }

(尽管对于更复杂的情况,也许功能解决方案最有意义)

于 2009-12-23T09:52:22.833 回答
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);

编辑2

啊..现在我明白你想要什么了..

我们可以稍微修改我上面的查询来做你想做的事

menuItemsList
.SelectMany(x => { //do something with x like printing it  
                    x.ChildrenItems 
                 })
.Select(p => { // do something with p like printing it 
                  p 
             });

基本上你可以做你想做的事情里面的元素{}

谢谢

于 2009-12-23T09:58:22.033 回答
1

我建议两种方法来实现这一点。您可以选择使用实用程序方法来获取所有项目,也可以实现访问者模式,尽管这意味着更改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());
        }
于 2009-12-23T09:59:02.117 回答
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
    }
于 2009-12-23T16:34:54.377 回答