当我尝试通过下面的属性
添加到列表时,我继续在子列表中获得空引用 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));
}
....
我不认为你想在每次点击这个属性时启动Children列表,那么每次调用代码需要填充它时我该怎么做才能添加到这个Node的子节点?
答案 0 :(得分:3)
你可以尝试这样的事情
public void AddChild(T childNode)
{
if(this.Children == null)
{
this.Children = new List<Node<T>>();
}
this.Children.Add(new Node<T>(childNode));
}
答案 1 :(得分:2)
添加默认构造函数。
public Node()
{
Children = new List<Node<T>>();
}
答案 2 :(得分: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));
}
答案 3 :(得分: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));
}
}
答案 4 :(得分:0)
您有两种选择:在构造函数中初始化集合,或者在AddChild中懒惰地初始化集合。 但基本上最好不要使用像这样的子系列,因为它是公开的。
答案 5 :(得分:0)
有几种不同的使用方案,具体取决于Children
属性是否应该返回数据的持久性快照,实时视图,或者明确无效的临时只读视图如果数据被修改,或者是临时只读视图,如果数据被修改,其行为将不被指定。
我在使用可变对象[例如List<T>
]时建议遵循的一个关键规则是,如果对某个对象的引用可能会暴露给可能会改变其状态某些方面的代码,那么应该有系统中最多只有一个(通常是一个)对象,该对象将该对象的状态视为其自身的一部分。 .NET和Java都没有提供任何约定来指示对象是否包含一个字段以封装其目标所持有的可变状态;必须手动跟踪它。
可变类型的读写属性仅适用于暴露属性的对象对所引用的对象没有所有权利益的情况。例如,List<Control>
封装其中包含引用的Control
个对象的标识;这些控制的可变方面(例如它们的位置)不构成List<Control>
状态的一部分。但是,与您的Node<T>
类似,它应该拥有Children
,因为将子项添加到SomeNode.Children
将被视为对SomeNode
状态的更改。因此,Children
属性应该是不可变的[不只是只读!]类型(在这种情况下所有权无关紧要),否则它应该是只读属性。否则,如果代码试图将Children
设置为某个其他对象所拥有的List<T>
,则无法避免模糊语义。
如果Children
作为List<T>
拥有的Node<T>
的只读引用公开,则必须在首次尝试之前创建新的List<T>
读取Children
属性可以返回(在List<T>
的构造函数中创建Node<T>
可能最简单)。另一种方法是定义一个私有类Node.ChildList
,它通过访问该节点的内部字段来保存对Node<T>
的引用并实现IList<T>
和ICollection
。使用这种方法,Node<T>
必须创建轻量级Node.ChildList
对象,即使没有添加任何对象[它可能在访问Children
时被懒惰地创建,但它可能更好在构造函数中创建它],但ChildList
或ICollection.Count
之类的IEnumerable<T>.GetEnumerator()
实现可以通过测试节点是否已经创建了一个集合来开始,如果没有,只需返回一些东西适合空集。
顺便提一下,如果T
是一个类类型而且许多节点只有一个子节点,那么Node<T>
存储Count
和Object
可能会有利而不是存储List<T>
。如果某个节点没有子节点,则Object[]
将保留null
;对于一个孩子,它会持有T
;对于更多孩子,可以是T[]
或List<T>
。在访问节点的子节点时,这将需要更复杂的类型检查逻辑,但对于具有零个或一个子节点的节点,它将消除一些堆分配。