1

我有一个自定义类型的对象(菜单),它可以包含相同类型的子元素,自然可以包含子元素等等。

我希望从这些对象中生成菜单和子菜单,但我想知道如何递归地遍历它们,这样我就不必对所有循环进行硬编码。我对递归不熟悉,有没有人能够阐明如何遍历 Menu 对象和所有底层对象?

示例代码:

public class Menu {
    public int MenuID { get; set; }
    public int Name { get; set; }
    public Menu[] _ChildMenus { get; set; }
}
4

3 回答 3

3

这是一种选择:

private void TraverseMenu(Menu menu)
{
    Output(string.Format("Now reading menu #{0}, named {1}", menu.MenuID, menu.Name));
    if (menu._ChildMenus != null)
    {
        foreach (Menu child in menu._ChildMenus)
        {
            TraverseMenu(child);
        }
    }
}

如果您需要知道每个菜单嵌套的“深度”程度,您可以添加一个级别参数:

private void TraverseMenu(Menu menu, int level)
{
    Output(string.Format("Now reading menu #{0}, named {1} in level {2}", menu.MenuID, menu.Name, level));
    if (menu._ChildMenus != null)
    {
        foreach (Menu child in menu._ChildMenus)
        {
            TraverseMenu(child, level + 1);
        }
    }
}

private void TraverseMenu(Menu menu)
{
    TraverseMenu(menu, 0);
}
于 2013-02-11T12:21:26.490 回答
0

出于渲染目的,您可以使用局部视图。

-- MenuView.cshtml

@model IEnumerable<Menu>

@if (Model != null && Model.Any())
{
    <ul>
        @foreach(var menu in Model)
        {
            <li>
                <a href='some/path/to/menu?id=@menu.MenuID'>@menu.Name</a>
                @Html.RenderPartial("MenuView", menu._ChildMenus)
            </li>
        }
    <ul>
}

编辑:由于主题从“C# 中的递归”中与 MVC 相关的内容发生了变化,这似乎有点无关紧要。

于 2013-02-11T12:27:16.640 回答
0

通常每个级别的菜单都需要一个与之关联的不同循环,例如第一级(例如:home、products、about_us)将水平显示在顶部,第二级将出现在相关父菜单项下方的下拉列表中,第三级将出现在第二级菜单项等下方。

因此,对于循环,您理论上不希望按级别拆分它们,同时保持与父菜单项的链接,以便显示它们。另一个更普遍的事情是树中的最高菜单通常被忽略为其他菜单项的容器元素,这允许您拥有多个顶级菜单,如下所示:

编辑: 修复了代码..随时返回编辑历史以了解我是多么的非天才:p

string html;
int lastLevel = 0;
void placeMenu( Menu menu, int level)
{
    // if level hasn't changed close last menu-item
    if( lastLevel == level ) html += "</li>";

    // if we're deeper, open a new <UL>
    else if( lastLevel < level ) html += "<ul>";

    // if we're less deep, close the last <UL> and it's parent menu-item
    else if( lastLevel > level ) html += "</ul></li>";

    // add current menu item without closing it's <LI> so the next itteration of the loop can add a submenu if needed
    html += "<li><a href='http://link/to/page'>" + menu.Name + "</a>";

    lastLevel = level;
}

void setupMenu( Menu menu, int level )
{
    foreach( var currentMenu in menu._ChildMenus )
    {
        // place current menu
        placeMenu( currentMenu, level + 1 );

        // place submenus
        setupMenu( currentMenu, level + 1 );
    }
}

string setupWholeMenu( Menu menu )
{
    setupMenu( menu, 0 );

    // close any elements left open by the recursive loop
    html = html + "</li></ul>";

    return html;
}

此代码旨在从您的菜单结构中创建一个普通的 html 无序级列表,这是在 HTML 中添加和设置菜单样式的标准方法,当您拥有这种格式的菜单时,请查看其中一些资源以设置菜单样式。在这里使用 HTML 无序列表的原因是,如果正确实施,如果禁用 Javascript 和 CSS(GSM 和屏幕阅读器适用于部分视力),这个列表结构仍然会显示,并且如果禁用 Javascript,它仍然适用于 IE6+ 的所有内容。

老实说,虽然在 MVC 中通常更容易以声明方式直接将菜单设置为 HTML 中的无序列表,然后如果将其放置在部分共享页面中或通过定义在任何地方显示它,则可以以多种方式设置它的样式它在您的布局页面中。如果您具有如上所示的动态菜单结构,这种方法仍然可以工作,只是您使用 Razor 构建列表,这可能也更容易。

另一点需要注意的是,在这种递归函数中,您应该使用StringBuilder,因为它比连接字符串更有效。然而,对于一个菜单结构(最多包含 30 个项目,每个项目由 2-3 个 concats 构建),这不会导致任何明显的延迟,只是将来要记住的事情。

于 2013-02-11T12:48:36.143 回答