簡體   English   中英

C#遞歸泛型數據結構搜索

[英]c# recursive generics data structure searching

已經為此苦苦掙扎了兩天,現在仍然很沮喪。 我有一個數據結構,該結構以可以容納其他容器的容器開始,最后是葉節點。 我正在尋找一種直接迭代類型的元素的方法,而無需將它們拉入另一個集合中,這樣我就可以對它們進行適當的操作,然后將結果結構保存出來。

下面的代碼是一個點頭版本,如果您在每個findElements函數上設置一個斷點,您會發現它會退出而不會遞歸。 這是在mono和ms運行時上進行的,所以我確定這不是我得到的東西,而是bug;)

同樣,功能理想上應該是

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

我認為您的代碼有點復雜,但是我可能不太了解您的目標。
無論如何,這里有一個樣本以“平鋪”方式掃描您的樹。 我還使用了非常小的代碼來演示操作方法,但是顯然您必須繼續工作。

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

希望這可以幫助。 干杯。

您希望它如何運作? 如果我正確理解它,那么從另一個函數調用時yield無效(因此,如果調用base.findElements,則不會得到任何結果)。 我建議沒有收益而重寫它。 為了避免創建許多列表,我將以這種方式將list作為參數傳遞:

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的實現是完全錯誤的。 基本容器類將看不到Lines屬性,而將使用空的Children屬性。 有兩種方法可以解決此問題:

  1. 你需要殺死_lines ,而是一起工作_children從基類(例如,你可以Collection<Line>的后代,這將是圍繞一個包裝_children類通過重寫InsertItem,SetItem,的removeItem和ClearItems並調用適當的方法_children ) 。

  2. 從Container中刪除_children ,而是使每個后代將實現的虛擬抽象函數IEnumerable GetChildElements()並返回其自己的子元素List<> 在doFindElements中,您可以調用該函數而不是_children 您甚至可以創建第二個基類,例如UntypedContainer:聲明List<Container> _children ,重寫GetChildElements()以返回_children_children繼承Document。 文件仍將從簡單的Container繼承,因為它具有自己的子級列表。

第二種方法更簡單更好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM