1

尝试通过下面的属性添加到列表时,我不断在子列表上获得空引用

    public class Node<T> where T : INode
    {
        private readonly T _item;

        public IList<Node<T>> Children { get; set; } 

        public Node(T item)
        {
            _item = item;
            ParentNodeId = item.ParentNodeId;
        }


        public void AddChild(T childNode)
        {
            Children.Add(new Node<T>(childNode));
        }
....

而且我认为您不会在每次遇到此属性时都启动子列表,那么每次调用代码需要填充它时,我应该怎么做才能添加到该节点的子节点中?

4

6 回答 6

4

你可以尝试这样的事情

    public void AddChild(T childNode)
    {
        if(this.Children == null)
        {
           this.Children = new List<Node<T>>();
        }
        this.Children.Add(new Node<T>(childNode));
    }
于 2013-04-01T08:14:07.857 回答
2

添加默认构造函数。

public Node()
{
    Children = new List<Node<T>>();
}
于 2013-04-01T08:12:14.420 回答
1

我认为在构造函数中添加初始化程序没有任何问题:

public Node(T item)
{
    _item = item;
    ParentNodeId = item.ParentNodeId;
    Children = new List<T>();
}

或者,如果你真的希望它是延迟加载的,在add方法中:

public void AddChild(T childNode)
{
    Children = Children ?? new List<T>();
    Children.Add(new Node<T>(childNode));
}
于 2013-04-01T08:14:35.617 回答
1

其他人提供了很好的答案,但我想指出,没有人给出进行延迟初始化的最佳方法:

public class Node<T> where T : INode
{
    private readonly T _item;
    private readonly Lazy<List<Node<T>>> _children = new Lazy<List<Node<T>>>(
        () => new List<Node<T>>());

    public IList<Node<T>> Children { get { return _children.Value; } }

    public Node(T item)
    {
        _item = item;
        ParentNodeId = item.ParentNodeId;
    }


    public void AddChild(T childNode)
    {
        Children.Add(new Node<T>(childNode));
    }
}
于 2013-04-01T12:09:17.467 回答
0

您有两个选择:在构造函数中初始化集合或在 AddChild 中延迟。但基本上最好有像这样的非空集合,因为它是公共的。

于 2013-04-01T08:10:09.407 回答
0

有几种不同的使用场景,这取决于Children属性是否应该返回数据的持久快照、实时视图或临时只读视图,如果数据被修改,将明确失效,或者临时只读视图,如果数据被修改,其行为将未指定。

在使用可变对象 [例如 ] 时,我建议遵循的一个关键规则List<T>是,如果对对象的引用可能会暴露给可能会改变其状态的某些方面的代码,那么最多应该有一个(通常正好是一个)系统中的对象,该对象将该对象的状态视为其自身的一部分。.NET 和 Java 都没有提供任何约定来指示对象是否持有一个字段,以封装其目标持有的可变状态;必须手动跟踪它。

可变类型的读写属性仅适用于明确公开该属性的对象对其所引用的对象没有所有权利益的情况。例如,aList<Control>封装了其引用包含在其中的对象的身份;Control这些控件的可变方面(例如它们的位置)不构成List<Control>. 然而,像你这样的东西Node<T>似乎应该拥有Children,因为添加一个孩子SomeNode.Children将被视为对SomeNode. 因此,Children属性应该是不可变的 [不仅仅是只读!] 类型(在这种情况下所有权无关紧要),否则它应该是只读属性。Children否则,如果代码试图设置为List<T>某个其他对象拥有的a ,就无法避免模糊语义。

如果作为对拥有的Childrena 的只读引用公开,则必须在第一次尝试读取属性返回之前创建一个 new (在 的构造函数中创建 可能是最简单的)。另一种方法是定义一个私有类,该类包含对 a 的引用并实现并访问该节点的内部字段。使用这种方法,即使没有添加任何对象,a也必须创建轻量级对象[它可以在访问时延迟创建,但在构造函数中创建它可能更好],但是诸如或之类的东西的实现List<T>Node<T>List<T>ChildrenList<T>Node<T>Node.ChildListNode<T>IList<T>ICollectionNode<T>Node.ChildListChildrenChildListICollection.CountIEnumerable<T>.GetEnumerator()可以首先测试节点是否已经创建了一个集合,如果没有,则简单地返回适合空集合的东西。

顺便说一句,如果T是一个类类型并且许多节点将只有一个孩子,那么Node<T>存储 aCount和 an可能Object比存储 a更有利List<T>。如果一个节点没有子节点,Object[]null; 对于一个孩子,它会持有一个T;对于更多的孩子,要么 aT[]要么 a List<T>。当访问节点的子节点时,这将需要更复杂的类型检查逻辑,但对于具有零个或一个子节点的节点,它将消除一些堆分配。

于 2013-04-01T16:26:29.413 回答