搜索模板化的树

时间:2010-05-11 23:51:23

标签: c# tree

所以我有2个接口:

可以有孩子的节点

public interface INode
{
    IEnumeration<INode> Children { get; }
    void AddChild(INode node);
}

派生的“数据节点”可以包含与之关联的数据

public interface IDataNode<DataType> : INode
{
    DataType Data;
    IDataNode<DataType> FindNode(DataType dt);
}

请记住,树中的每个节点都可以使用与其关联的不同数据类型作为其数据(因为INode.AddChild函数只接受基本的INode)

以下是IDataNode接口的实现:

internal class DataNode<DataType> : IDataNode<DataType>
{
    List<INode> m_Children;

    DataNode(DataType dt)
    {
        Data = dt;
    }

    public IEnumerable<INode> Children
    {
        get { return m_Children; }
    }

    public void AddChild(INode node)
    {
        if (null == m_Children)
            m_Children = new List<INode>();

        m_Children.Add(node);
    }   

    public DataType Data { get; private set; }

问题是如何在不知道树中将遇到哪种DataType的情况下实现FindNode函数?

    public IDataNode<DataType> FindNode(DataType dt)
    {
        throw new NotImplementedException();    
    }
}

你可以想象这样的事情无法解决

    public IDataNode<DataType> FindNode(DataType dt)
    {
        IDataNode<DataType> result = null;

        foreach (var child in Children)
        {
            if (child is IDataNode<DataType>)
            {
                var datachild = child as IDataNode<DataType>;

                if (datachild.Data.Equals(dt))
                {
                    result = child as IDataNode<DataType>;
                    break;
                }
            }
            else 
            {
                 // What??
            }

            // Need to recursively call FindNode on the child
            // but can't because it could have a different
            // DataType associated with it. Can't call FindNode 
            // on child because it is of type INode and not IDataNode
            result = child.FindNode(dt); // can't do this!

            if (null != result)
                break;
       }

       return result; 
    }

当我知道我使用的特定树将具有哪种DataType时,我是唯一的选择吗?也许我会以错误的方式解决这个问题,所以任何提示都会受到赞赏。谢谢!

2 个答案:

答案 0 :(得分:2)

首先,您需要将FindNode方法放在INode中。否则,在找到类型为DataType的节点之前,您找不到某种类型DataType ...的节点。即使您对知道的对象的引用是DataNode<X>,如果有人告诉您找到DataNode<Y>,这也无济于事。

现在可以采取两条道路:如果您想要DataNode进行模板化,那么您需要在编译时知道树中所有可能的数据类型。如果您知道,可以使用通用DataNode。如果您有可能想要找到某个类型的数据只能在运行时为您所知的节点(例如,从您无法控制的某些方法的返回值),那么您就不能使用泛型。

我将在下面说明通用解决方案。

public interface INode
{
    IEnumerable<INode> Children { get; }
    IDataNode<DataType> FindNode<DataType>(DataType value);
    void AddChild(INode node);
}

public interface IDataNode<DataType> : INode
{
    DataType Data { get; }
}

INode.FindNode可以像这样实现:

public IDataNode<DataType> FindNode<DataType> (DataType value) {
    // If we are searching for ourselves, return this
    var self = this as IDataNode<DataType>;
    if (self != null && self.Data.Equals(value)) {
        return self;
    }

    // Otherwise:
    // 1. For each of our children, call FindNode on it. This will
    //    find the target node if it is our child, since each child
    //    will check if it is the node we look for, like we did above.
    // 2. If our child is not the one we are looking for, FindNode will
    //    continue looking into its own children (depth-first search).
    // 3. Return the first descendant that comes back and is not null.
    //    If no node is found, FirstOrDefault means we will return null.
    return this.children.Select(c => c.FindNode(value))
                        .FirstOrDefault(found => found != null);
}

我不得不说上面用LINQ进行的递归实现可能太聪明了,也许不是很容易理解。它总是可以用foreach编写,以使其更清晰。

答案 1 :(得分:0)

使用通用功能:

public IDataNode<DataType> FindNode<DataType>(DataType dt)
{
    IDataNode<DataType> result = null;

    foreach (var child in Children)
    {
        if (child is IDataNode<DataType>)
        {
            var datachild = child as IDataNode<DataType>;

            if (datachild.Data.Equals(dt))
            {
                result = child as IDataNode<DataType>;
                break;
            }
        }
        else 
        {
             // it's not a DataType You're looking for, so ignore it!
        }
   }

   return result; 
}

然后你这样称呼它:

var resultsStr = tree.FindNode<string>("Hello");
var resultsInt = tree.FindNode<int>(5);
var resultsCust = tree.FindNode<MyCustomClass>(new MyCustomClass("something"));