简体   繁体   中英

Recursion in C#

I have an object of a custom type I've made (Menu) which can contain child elements of the same type, which naturally can contain child elements and so on.

I wish to generate menus and submenus out of these objects, but I'm wondering how I can traverse them recursively so that I don't have to hard code all my loops. I am a stranger to recursion, is anyone able to shed some light on how to traverse the Menu object and all the underlying objects?

Example code:

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

Here is one option:

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

If you need to know how "deep" each menu is nested you can add a level parameter:

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

For rendering purpose you can use partial views.

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

EDIT: Since topic changed from something related to MVC from "Recursion in C#", this may seem a little bit irrelevant.

Normally each level of the menu needs a different loop associated with it, for example the first level (eg: home, products, about_us) would display horizontally accross the top, the second level would appear in drop downs beneath the relevant parent menu-item, tertiary levels will appear below the 2nd level menu-item etc..

So for the loops you'd theoretically wan't to split them by level, while maintaining a link back to the parent menu-item so they can be shown. Another more general thing is that the highest menu in the tree is often ignored as a container element for other menu items, this allows you to have more than 1 top level menu something like the following:

EDIT: Fixed the code.. feel free to turn back the edit history to find out how epically non-genius I am :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;
}

this code is designed to make a normal html unordered-level list from your menu structure, this is the standard way to add and style menus in HTML, when you have your menu in this format check out some of these resources to style your menu. The reason to use HTML unordered lists here is that when implemented correctly this list structure will still show up if Javascript & CSS are dissabled (GSM & screen-readers for the partially sighted) and still work in everything IE6+ if Javascript is dissabled.

To be honest though in MVC it's usually allot easier just to setup your menu in a declarative way as an unordered list in HTML directly, you can then style it in multiple ways if you place it in a partial shared page or show it everywhere by defining it in your Layout page. This approach can still work if you have a dynamic menu structure as it would seem above, only you'd build the list with Razor which is probably easier too.

Another point of note is that in this kind of recursive function you should use StringBuilder as it's way more efficient then concatenating strings. However for a menu structure (containing up to say 30 items each built with 2-3 concats) this won't cause any notable delay, just something to keep in mind for the future.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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