【问题标题】:Passing parent node in recursion递归传递父节点
【发布时间】:2016-08-26 09:28:39
【问题描述】:

当该对象处于递归“构造”状态时,有什么方法可以将对象(或引用)传递给函数?我想在他的孩子中保留我的父节点。

以某种方式调用地点:

NavigationTreeNode node = FindNode(Resources.ShellView_TreeNodeBusinessRules);
var tmpList = LoadNodes(xDoc.Descendants("root").Elements("tab"), node);

这是递归:

 private List<NavigationTreeNode> LoadNodes(IEnumerable<XElement> nodes, NavigationTreeNode parentNode)
 {
     return nodes.Select(x => 
         new NavigationTreeNode(x.Attribute("display-name").Value, parentNode)
         { 
            TabName = x.Attribute("tab-name").Value,
            // Here I want to send object that is now under 
            // construction, or maybe reference
            ChildNodes = LoadNodes(x.Elements("tab"), /* ?? */)
         }).ToList();
 }

【问题讨论】:

    标签: c# linq recursion linq-to-xml


    【解决方案1】:

    您可以添加{} 来指定 lambda 表达式的范围,然后您可以将新对象保存到变量中,设置其属性然后返回:

    return nodes.Select(x => 
    {
        var obj = new NavigationTreeNode(x.Attribute("display-name").Value, parentNode)
        { 
            TabName = x.Attribute("tab-name").Value
        };
        obj.ChildNodes = LoadNodes(x.Elements("tab"), obj);
        return obj;
    }).ToList();
    

    【讨论】:

    • @MrinalKamboj - 没想到 :) 不是问题。但我假设当 XElement 没有“选项卡”元素时,在下一次调用中它将是 Select 在一个空列表中 -> 并且将停止
    • @Gilad Green 当然谢谢,我的假设是当nodes 集合为空时,OP 表示它可以正常工作,所以这意味着没有无限循环
    • @MrinalKamboj 空元素。 Linq 对此负责。
    • @p__d - 已恢复 :)
    • @MrinalKamboj - 我也被你的评论吓了一跳,但现在我们知道了答案:)
    【解决方案2】:

    坦率地说,如果您重新设计导航类,这可能会做得更好。对于这种特殊情况,该结构不适合使用 linq 递归地重建它。虽然 linq 可以用来初始化递归结构,但是当结构是双向链接时就不能有效地利用了。

    通常,在初始化结构时创建要使用的子级列表是错误的。当您提供对内部结构的直接访问时,它会破坏封装。无论如何,您都应该提供操作这些方法的方法。

    例如,像这样的结构,它会让事情变得更容易:

    class NavigationTreeNode
    {
        private List<NavigationTreeNode> childNodes = new List<NavigationTreeNode>();
        public NavigationTreeNode(string tabName, string displayName)
        {
            TabName = tabName;
            DisplayName = displayName;
        }
        public NavigationTreeNode Parent { get; private set; }
        public string TabName { get; }
        public string DisplayName { get; }
        public IReadOnlyList<NavigationTreeNode> ChildNodes => childNodes.AsReadOnly();
    
        public void Add(NavigationTreeNode childNode)
        {
            if (childNode.Parent != null)
                childNode.Parent.Remove(childNode);
            childNodes.Add(childNode);
            childNode.Parent = this;
        }
    
        private void Remove(NavigationTreeNode childNode)
        {
            childNodes.Remove(childNode);
            childNode.Parent = null;
        }
    }
    

    那么加载函数应该是这样的:

    private NavigationTreeNode LoadNode(XElement node)
    {
        var treeNode = new NavigationTreeNode((string)node.Attribute("tab-name"), (string)node.Attribute("display-name"));
        foreach (var tab in node.Elements("tab"))
            treeNode.Add(LoadNode(tab));
        return treeNode;
    }
    

    并且根据您想在设计上走多远,您可以完全消除对循环的需求,并完全使用 linq 编写它。我会把它留给你作为练习。

    【讨论】:

      猜你喜欢
      • 2017-05-04
      • 2018-12-31
      • 1970-01-01
      • 1970-01-01
      • 2017-07-29
      • 2011-10-02
      • 2011-11-12
      • 2011-11-12
      • 1970-01-01
      相关资源
      最近更新 更多