在模板树上搜索

发布于 2024-09-01 00:44:53 字数 2255 浏览 0 评论 0原文

所以我有 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; 
    }

当我知道我使用的特定树将具有什么类型的数据类型时,我唯一的选择是这样做吗?也许我以错误的方式处理这个问题,所以任何提示都会受到赞赏。谢谢!

So I have 2 interfaces:

A node that can have children

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

And a derived "Data Node" that can have data associated with it

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

Keep in mind that each node in the tree could have a different data type associated with it as its Data (because the INode.AddChild function just takes the base INode)

Here is the implementation of the IDataNode interface:

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

Question is how do I implement the FindNode function without knowing what kinds of DataType I will encounter in the tree?

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

As you can imagine something like this will not work out

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

Is my only option to do this when I know what kinds of DataType a particular tree I use will have? Maybe I am going about this in the wrong way, so any tips are appreciated. Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

江南烟雨〆相思醉 2024-09-08 00:44:53

首先,您需要将FindNode方法放入INode中。否则,在找到 DataType 类型的节点之前,您无法找到某种类型 DataType 的节点。即使您知道的对象是 DataNode 的引用,如果有人告诉您查找 DataNodeDataNode,这对您也无济于事;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 编写,以使其更清晰。

First of all, you need to put the FindNode method in INode. Otherwise, you cannot find a node of some type DataType... before having found a node of type DataType. Even if you have a reference to an object that you know is a DataNode<X>, this won't help you if someone tells you to find a DataNode<Y>.

There are now two roads you may take: if you want DataNode to be templated, then you need to know all possible types of data in the tree at compile time. If you know that, you can use a generic DataNode. If there's a chance that you may want to find a node with data of some type that will only become known to you at runtime (e.g. from the return value of some method that you do not control) then you cannot use generics.

I will illustrate the generic solution below.

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 could be implemented like this:

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

I have to say that the above recursive implementation with LINQ tries perhaps to be too clever and is maybe not very easy to understand. It could always be written with foreach, to make it more clear.

独守阴晴ぅ圆缺 2024-09-08 00:44:53

使用通用函数:

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

Use a Generic Function:

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

Then you call it like this:

var resultsStr = tree.FindNode<string>("Hello");
var resultsInt = tree.FindNode<int>(5);
var resultsCust = tree.FindNode<MyCustomClass>(new MyCustomClass("something"));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文