c#中自定义委托的示例用法

时间:2011-01-17 01:38:39

标签: c# delegates tree parent-child

我在SO上发现了this关于C#中树实现的问题。我不知道代表,我想知道如何使用以下代码来实现树。另外,保持跟踪父节点的最有效方法是什么?

delegate void TreeVisitor<T>(T nodeData);

class NTree<T>
{
    T data;
    LinkedList<NTree<T>> children;

    public NTree(T data)
    {
        this.data = data;
        children = new LinkedList<NTree<T>>();
    }

    public void addChild(T data)
    {
        children.AddFirst(new NTree<T>(data));
    }

    public NTree<T> getChild(int i)
    {
        foreach (NTree<T> n in children)
            if (--i == 0) return n;
        return null;
    }

    public void traverse(NTree<T> node, TreeVisitor<T> visitor)
    {
        visitor(node.data);
        foreach (NTree<T> kid in node.children)
            traverse(kid, visitor);
    }        
}

3 个答案:

答案 0 :(得分:3)

delegate基本上可以让你谈论符合某些标准的任意函数,而不必确切地知道你正在谈论的函数。

考虑以下代码遍历树,并对每个元素执行操作:

void DoSomethingToAllNodes(NTree<T> node)
{
    DoSomething(node);
    foreach (var child in node.Children)
        DoSomethingToAllNodes(child);
}

它有效,但它非常不灵活。我们必须为我们想要在节点上执行的每个不同操作重新实现该方法:

void DoSomethingElseToAllNodes(NTree<T> node)
{
    DoSomethingElse(node);
    foreach (var child in node.Children)
        DoSomethingElseToAllNodes(child);
}

相反,我们可以声明一个委托来表示“任何一个NTree<T>作为参数,并且不返回任何内容的方法”,然后我们可以接受一个满足该参数的方法。然后,我们可以实现一个处理所有可能性的单Traverse方法,而不必为每个不同的操作重新实现它。

答案 1 :(得分:1)

如果你完全熟悉c或c ++,可以将委托视为类型安全的函数指针。这就是我如何设法了解代表与大多数人进行对话的想法。

答案 2 :(得分:0)

您的代码所做的是声明delegate,它基本上是方法的签名。 traverse()方法接受任何函数,其签名与您声明为参数的委托匹配并调用它。为了娱乐和愉悦,一切都是通用的,以便启动。

delegate void TreeVisitor<T>(T nodeData);

//  This function takes a handle to a method as a parameter,
//  and invokes that method for each node
public void traverse(NTree<T> node, TreeVisitor<T> visitor) {
    visitor(node.data);
    ...
}

可以像:

一样调用
public class MyEvilPlans {

    public void WreakHavoc<int>(int nodeData) {
        Console.WriteLine("The secret to life is:  {0}", nodeData.ToString());
    }

    public void PlayWithTree() {
        NTree<int> tree = new NTree<int>();

        //  Initialize tree

        //  If the tree has 47 nodes, WreakHavoc will be called 47 times,
        //  once for each node in the tree.
        tree.traverse(WreakHavoc);
    }

}

访问者模式允许您决定要为树的每个节点执行哪些代码...其他人控制树的实现,但您通过预定义的合同(委托)控制对树行为的响应。在某种程度上,它打破了封装,因为你知道你正在调用的方法的内部细节:它将为每个节点回调你。但这就是访客模式的全部内容:)

要回答问题的第二部分,如何跟踪父节点......你所拥有的是非二叉树。为了能够从任何节点遍历树,你必须给每个NTree一个NTree类型的成员称为'parent'。这应该由NTree的构造函数设置,以便您拥有:

//  Added the handle to 'this'
children.AddFirst(new NTree<T>(data, this));

您可以找到关于非二叉树here的精彩讨论。