[英]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屬性。 有兩種方法可以解決此問題:
你需要殺死_lines
,而是一起工作_children
從基類(例如,你可以Collection<Line>
的后代,這將是圍繞一個包裝_children
類通過重寫InsertItem,SetItem,的removeItem和ClearItems並調用適當的方法_children
) 。
從Container中刪除_children
,而是使每個后代將實現的虛擬抽象函數IEnumerable GetChildElements()
並返回其自己的子元素List<>
。 在doFindElements中,您可以調用該函數而不是_children
。 您甚至可以創建第二個基類,例如UntypedContainer:聲明List<Container> _children
,重寫GetChildElements()
以返回_children
並_children
繼承Document。 文件仍將從簡單的Container繼承,因為它具有自己的子級列表。
第二種方法更簡單更好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.