繁体   English   中英

C#中的递归

[英]Recursion in C#

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

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

示例代码:

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

这是一个选项:

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

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

-- 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相关的内容发生了变化,这似乎有点无关紧要。

通常,菜单的每个级别都需要与其关联的不同循环,例如第一级(例如: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构建),这不会导致任何明显的延迟,这只是为了将来要记住的事情。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM