c#递归泛型数据结构搜索

时间:2009-11-08 16:32:46

标签: c# generics recursion

现在已经挣扎了几天,仍然难倒。 我有一个数据结构,从容器可以容纳其他容器,最终叶子节点开始。我正在寻找一种方法来直接迭代一个类型的元素,而不是将它们拉到另一个集合中,这样我就可以对它们进行操作,然后将结果结构保存起来。

下面的代码是一个点头版本,如果你在每个findElements函数上设置一个断点,你会发现它没有递归而退出。这是在单声道和ms运行时,所以我确定这是我没有得到的东西,而不是一个错误;)

另外,理想情况下该功能应该是

IEnumerable<object> findElements<T>();

但是我无法让演员在这一行上工作:

if (this is T) yield return this;

理想情况下应

if (this is T) yield return (T)this;

感谢任何建议/清晰度/光明

using System;
using System.Collections.Generic;
using System.Text;

namespace covariantTest {
class MainClass {
    public static void Main(string[] args) {
        Console.WriteLine("Starting");
        Document root = new Document("rooty");
        root.Children.Add(new File("file 1"));
        root.Children.Add(new File("file 2"));
        Document doc2 = new Document("doc2");
        File file3 = new File("file 3");
        file3.Lines.Add(new Line("line 1 file 3"));
        file3.Lines.Add(new Line("line 2 file 3"));
        doc2.Children.Add(file3);
        File file4 = new File("file 4");
        file4.Lines.Add(new Line("stuff about stuff"));
        file4.Lines.Add(new Line("Babylon n ting"));
        file4.Lines.Add(new Line("last line"));
        doc2.Children.Add(file4);
        root.Children.Add(doc2);

        // find the lines
        foreach (object obj in root.findElements<Line>()) {
            Line line = obj as Line;
            Console.WriteLine(line.Contents);
        }
        // done
        Console.WriteLine("Press enter to finish");
        Console.ReadLine();
    }
}// Main

#region classes
public class Line : ISearchable {
    private string _contents = string.Empty;

    public Line() {}
    public Line(string contents) {
        _contents = contents;
    }
    #region properties
    public string Contents {
        get { return _contents; }
        set { _contents = value; }
    }
    #endregion properties
    public IEnumerable<object> findElements<T>() {
        if (this is T) yield return this;
    }
}// Line

public class File : Container {
    private List<Line> _lines = new List<Line>();

    public File() : base() {}
    public File(string name) : base(name) {}

    #region properties
    public List<Line> Lines {
        get { return _lines; }
        set { _lines = value; }
    }
    #endregion properties
    public override IEnumerable<object> findElements<T>() {
        if (this is T) yield return this;
        else base.findElements<T>();
    }
}// File

public class Document : Container {

    public Document() : base() {}
    public Document(string name) : base(name) {}

    public override IEnumerable<object> findElements<T>() {
        if (this is T) yield return this;
        else base.findElements<T>();
    }

}// Document

public abstract class Container : ISearchable {
    private string _name = string.Empty;
    private List<Container> _children = new List<Container>();

    public Container() {}
    public Container(string name) {
        _name = name;
    }
    #region properties
    public string Name { 
        get { return _name; } 
        set { _name = value; }
    }
    public List<Container> Children { 
        get { return _children; } 
        set { _children = value; }
    }
    #endregion properties
    #region interfaces
    public virtual IEnumerable<object> findElements<T>() {
        if (this is T) yield return this;
        foreach (Container item in _children) {
            item.findElements<T>();
        }
    }
    #endregion interfaces
}// Container
#endregion classes

#region interfaces
public interface ISearchable {
    IEnumerable<object> findElements<T>();
}
#endregion interfaces
}// namespace

2 个答案:

答案 0 :(得分:0)

我认为你的代码有点复杂,但我可能很难理解你的目标。
无论如何,这是一个以“平面”方式扫描你的树的样本。我还使用了一个非常小的代码来展示 - 但显然你必须继续工作。

namespace ConsoleApplication3
{
    //this is a node of your tree, but you may add whatever you want inside
    class Item
    {
        public List<Item> Items { get; set; }
    }


    class Program
    {
        static void Main(string[] args)
        {
            //define the tree structure
            var tree = new Item();

            // (complete your tree-structrure)

            //define the action delegate
            Action<Item> action = (item) => Console.WriteLine(item);

            //scan the hierarchy
            Scan(
                tree,
                typeof(Item),
                action);
        }


        //here is the flat-scan function, the "typeToFind" here is just
        //for example and have very little sense to be in
        static void Scan(
            Item startItem,
            Type typeToFind,
            Action<Item> action)
        {
            var temp = new List<Item>();
            temp.Add(startItem);

            while (temp.Count > 0)
            {
                var item = temp[0];
                temp.RemoveAt(0);

                if (typeToFind.IsInstanceOfType(item))
                {
                    action(item);
                }

                temp.AddRange(item.Items);
            }
        }
    }
}

希望这会有所帮助。欢呼声。

答案 1 :(得分:0)

您认为它如何运作?如果我理解正确,那么当从另一个函数调用时,yield不起作用(所以如果你调用base.findElements那么你就不会从中得到任何结果)。我建议不加收益地重写它。为了避免创建许多列表,我会以这种方式将列表作为参数传递:

public interface ISearchable {
    void doFindElements<T>(List<T> putThemHere);
}

// this is extender for syntactical sugar
public static class SearchableExtender
{
    public static IEnumerable<T> findElements<T>(this ISearchable obj)
    {
        List<T> result = new List<T>();
        obj.doFindElements(result);
        return result;
    }
}

public abstract class Container : ISearchable {
...
    public virtual void doFindElements<T>(List<T> putThemHere)
    {
        if (this is T) putThemHere.Add(this);
        foreach (Container item in _children) { item.doFindElements(putThemHere); }
    }
...
}

顺便说一下,你不需要在Document中覆盖doFindElements,从Container继承的版本就可以了,因为“this”在这里意味着一个Document。 File的实现完全错误。 Base Container类不会看到Lines属性,而是使用空的Children属性。有两种方法可以解决这个问题:

  1. 你需要杀死_lines,而是使用基类中的_children(例如,你可以使Collection<Line>后代成为{{1}的包装器通过重写InsertItem,SetItem,RemoveItem和ClearItems并调用_children)的适当方法来调用类。

  2. 从Container中删除_children,而不是创建每个后代将实现的虚拟抽象函数_children并返回其自己的IEnumerable GetChildElements()个子元素。在doFindElements中,您将调用该函数而不是List<>。您甚至可以创建第二个基类,例如UntypedContainer:将声明_children的容器,覆盖List<Container> _children以返回GetChildElements()并从中继承Document。文件仍将从简单的Container继承,因为它有自己的子列表。

  3. 第二种方式更简单,更好。