C# 如何在适当的位置行走和过滤树木?
给定一个由下面的类和数据定义的复杂对象树,有没有一种方法可以在树中进行适当的遍历,应用过滤器,这样我就不必克隆树对象了C# 如何在适当的位置行走和过滤树木?,c#,C#,给定一个由下面的类和数据定义的复杂对象树,有没有一种方法可以在树中进行适当的遍历,应用过滤器,这样我就不必克隆树对象了 出于发布目的,我简化了该类,但假设由于树节点的复杂性,在内存中克隆对象将是一个负担 这将在多线程环境中调用,因此我们假设删除节点或在树本身中设置属性会导致问题 我不是在寻找BFS或DFS遍历,因为我希望保持树结构不变。这个 类别: public class Menu { public string Text { get; set; } public IEnu
- 出于发布目的,我简化了该类,但假设由于树节点的复杂性,在内存中克隆对象将是一个负担李>
- 这将在多线程环境中调用,因此我们假设删除节点或在树本身中设置属性会导致问题
public class Menu
{
public string Text { get; set; }
public IEnumerable<Menu> Children { get; set; }
}
公共类菜单
{
公共字符串文本{get;set;}
公共IEnumerable子项{get;set;}
}
样本输入:
Root
| A1
| | A2
| | B1
| C1
| | A3
| | | A4
| | B2
| D1
| | B3
根
|A1
|| A2
|| B1
|C1
|| A3
|| | A4
|| B2
|D1
|| B3
给定“a”的筛选器,所需结果应包括每个本身为“a”或包含“a”的节点:
Root
| A1
| | A2
| C1
| | A3
| | | A4
根
|A1
|| A2
|C1
|| A3
|| | A4
我尝试了使用递归函数和IEnumerable函数查看不同的方法,但我不知道如何在不修改菜单实例的情况下将迭代器传递给子对象
private IEnumerable<Menu> BuildMenu(Menu menu)
{
foreach (var i in menu.Children)
{
if (/* item is filtered*/)
yield break;
// How to pass in current iterator and criteria without NEW()'ing an object?
yield return new Menu()
{
Text = menuWithChildren.Text,
Children = BuildMenu(menuWithChildren)
};
}
}
private IEnumerable构建菜单(菜单菜单)
{
foreach(menu.Children中的变量i)
{
如果(/*项已筛选*/)
屈服断裂;
//如何传入当前迭代器和条件而不新建()对象?
返回新菜单()
{
Text=menuWithChildren.Text,
Children=BuildMenu(menuWithChildren)
};
}
}
这是可能的,还是我的要求无效?感谢阅读。为了“就地”筛选列表(即,无需重建树/节点),您可以为每个节点添加属性“IsVisible”,并适当设置值 为了向下传递过滤逻辑,可以使用
Func
形式的谓词作为过滤函数的参数
下面是一个递归过滤函数,它将完全执行以下操作:
private static bool Filter(Menu item, Func<Menu,bool> predicate)
{
bool isVisible = predicate(item) ;
bool isChildrenVisible = (item.Children != null && item.Children.Count(c => Filter(c,predicate))>0);
item.IsVisible = isVisible || isChildrenVisible;
return isVisible;
}
我准备了一个实例来演示这一点:对于您的需求,您必须保持不变性。唯一的方法是创建初始树的视图(或投影) 最好的方法是为仅包含对原始菜单对象的引用的视图创建最小代理类
class MenuProxy
{
public Menu Reference {get; set;}
public IEnumerable<MenuProxy> Children {get; set;}
}
这是一个完整的例子
private IEnumerable<MenuProxy> BuildFilteredView(Menu menu)
{
foreach (var i in menu.Children)
{
if (/* item is filtered*/)
yield break;
// How to pass in current iterator and criteria without NEW()'ing an object?
yield return new MenuProxy()
{
Reference = menuWithChildren,
Children = BuildMenu(menuWithChildren)
};
}
}
private IEnumerable BuildFilteredView(菜单)
{
foreach(menu.Children中的变量i)
{
如果(/*项已筛选*/)
屈服断裂;
//如何传入当前迭代器和条件而不新建()对象?
收益率返回新MenuProxy()
{
Reference=menuWithChildren,
Children=BuildMenu(menuWithChildren)
};
}
}
在不重建每个节点的情况下,如何筛选列表?在每个节点上都有一个Visible
属性并进行适当的设置就足够了吗?别忘了c#是通过引用工作的var object=anotherObject
only duplicates reference.@Jamiec-我不想通过删除属性来筛选列表,我想以只返回筛选项的方式遍历树。不,您误解了-节点有一组子节点,不重建其父节点就无法筛选该子节点列表(不修改菜单实例
)。我建议了一种解决方法(我正在编写一些代码以获得答案),它只是在每个节点上设置一个可见的
属性为什么不构建一个(小的)只引用原始树中的节点的分离筛选树?不幸的是,这在多线程环境中不起作用,因为可视标志可以由两个不同的用户在两个不同的上下文中设置…因此我关心的是更改对象。请注意,由于您构造此代码的方式,它永远不会变短circuit,这意味着始终需要遍历整个树,而实际上,当您知道节点需要可见时,只要任何谓词返回true,您就可以立即停止。@MattMurrell,然后强制用户同步对对象的访问。
yield return new MenuProxy()
{
Reference = i,
Children = BuildMenu(i)
};
private IEnumerable<MenuProxy> BuildFilteredView(Menu menu)
{
foreach (var i in menu.Children)
{
if (/* item is filtered*/)
yield break;
// How to pass in current iterator and criteria without NEW()'ing an object?
yield return new MenuProxy()
{
Reference = menuWithChildren,
Children = BuildMenu(menuWithChildren)
};
}
}