现在已经挣扎了几天,仍然难倒。 我有一个数据结构,从容器可以容纳其他容器,最终叶子节点开始。我正在寻找一种方法来直接迭代一个类型的元素,而不是将它们拉到另一个集合中,这样我就可以对它们进行操作,然后将结果结构保存起来。
下面的代码是一个点头版本,如果你在每个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
答案 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属性。有两种方法可以解决这个问题:
你需要杀死_lines
,而是使用基类中的_children
(例如,你可以使Collection<Line>
后代成为{{1}的包装器通过重写InsertItem,SetItem,RemoveItem和ClearItems并调用_children
)的适当方法来调用类。
从Container中删除_children
,而不是创建每个后代将实现的虚拟抽象函数_children
并返回其自己的IEnumerable GetChildElements()
个子元素。在doFindElements中,您将调用该函数而不是List<>
。您甚至可以创建第二个基类,例如UntypedContainer:将声明_children
的容器,覆盖List<Container> _children
以返回GetChildElements()
并从中继承Document。文件仍将从简单的Container继承,因为它有自己的子列表。
第二种方式更简单,更好。