编写封装 parent/child 数据结构的更简单方法?
Easier way to write encapsulated parent/child data structure?
时不时发现自己经常写一个"parents"和"children"的数据结构,其中:
- A parent 引用了 0 到 N 个不同的 children.
- A child 引用了 0 parent 或 1 parent.
- 参考必须是相互的。对于任何给定的 parent,它引用的任何 child 也必须引用给定的 parent。对于任何给定的 child,它引用的 parent 必须引用给定的 child。
- 除了使用反射之外,不可能通过使用可从两个 class 声明(非
private
)之外访问的成员来违反上述规则。
在实现这样的事情之前可能采取的心理步骤可能从这样的事情开始:
public class Parent
{
private readonly List<Child> _children = new List<Child>();
public readonly ReadOnlyCollection<Child> Children = _children.AsReadOnly();
}
public class Child
{
private Parent _parent;
public Parent Parent
{
get
{
return _parent;
}
set
{
if(value == _parent)
return;
if(_parent != null)
{
_parent._children.Remove(this);
_parent = null;
}
if(value != null)
{
value._children.Add(this);
_parent = value;
}
}
}
}
当然,这不会编译,因为 Parent._children
是 private
。但是,您不想将其设为私有,因为允许 Child
或 Parent
之外的访问可能会违反实施或其他地方的规则。
所以,我想到的一个解决方案是将 Child
嵌套在 Parent
中。嵌套的 classes 可以访问嵌套在 class 中的私有成员:
public class Parent
{
private readonly List<Child> _children = new List<Child>();
public readonly ReadOnlyCollection<Child> Children = _children.AsReadOnly();
public class Child
{
private Parent _parent;
public Parent Parent
{
get
{
return _parent;
}
set
{
if(value == _parent)
return;
if(_parent != null)
{
_parent._children.Remove(this);
_parent = null;
}
if(value != null)
{
value._children.Add(this);
_parent = value;
}
}
}
}
}
我要问的问题是,是否有任何替代方法可以实现相同的目标,并且比这种方法有更少或更小的缺点?我发现这种方法的主要缺点是:
- 这会导致脚本变大,但使用
partial
会有所帮助。
- 这可能导致比预期更深的嵌套。
- 这会导致冗长的 class 名称。要在
Parent
之外访问 Child
,您必须使用 Parent.Child
。在 class 名称很长的情况下,尤其是在使用泛型时,这可能会导致代码非常难看。
- 泛型的使用(例如,实现 compile-time 类型安全)会变得混乱。这似乎至少部分源于
Child
是嵌套的并且 Parent<T1>.Child
是与 Parent<T2>.Child
不同的类型的事实,并且当您希望类型安全是相互的时,这可能会导致非常丑陋使用泛型,或者需要回退到使用 runtime-enforced 类型安全(虽然它可以被封装掉,通常,例如,使用 non-generic 抽象基,其中 public
访问器代替 protected
).
附带说明一下,这可能是一个很好的例子,说明使用 friend
扩展访问权限可以简化这些问题!
正如我在评论中所述,在父项中添加一个 Remove 方法,这至少不会暴露整个子项列表。类似于:
public class Parent
{
private readonly List<Child> _children = new List<Child>();
public readonly ReadOnlyCollection<Child> Children = _children.AsReadOnly();
public void Remove(Child child)
{
if(child !=null)
{
_children.Remove(child);
}
}
}
当然,如评论和之前的回答所述,您还需要封装将子项添加到父项,这需要 public Add(Child)
方法
我喜欢使用通用的私有接口来公开如下属性:
public class SomeBigOuterClass {
private interface IChildFriend {
void SetParent(Parent parent);
}
public class Parent {
}
public class Child : IChildFriend {
void IChildFriend.SetParent(Parent parent) {
this.parent = parent;
}
private Parent parent;
}
}
时不时发现自己经常写一个"parents"和"children"的数据结构,其中:
- A parent 引用了 0 到 N 个不同的 children.
- A child 引用了 0 parent 或 1 parent.
- 参考必须是相互的。对于任何给定的 parent,它引用的任何 child 也必须引用给定的 parent。对于任何给定的 child,它引用的 parent 必须引用给定的 child。
- 除了使用反射之外,不可能通过使用可从两个 class 声明(非
private
)之外访问的成员来违反上述规则。
在实现这样的事情之前可能采取的心理步骤可能从这样的事情开始:
public class Parent
{
private readonly List<Child> _children = new List<Child>();
public readonly ReadOnlyCollection<Child> Children = _children.AsReadOnly();
}
public class Child
{
private Parent _parent;
public Parent Parent
{
get
{
return _parent;
}
set
{
if(value == _parent)
return;
if(_parent != null)
{
_parent._children.Remove(this);
_parent = null;
}
if(value != null)
{
value._children.Add(this);
_parent = value;
}
}
}
}
当然,这不会编译,因为 Parent._children
是 private
。但是,您不想将其设为私有,因为允许 Child
或 Parent
之外的访问可能会违反实施或其他地方的规则。
所以,我想到的一个解决方案是将 Child
嵌套在 Parent
中。嵌套的 classes 可以访问嵌套在 class 中的私有成员:
public class Parent
{
private readonly List<Child> _children = new List<Child>();
public readonly ReadOnlyCollection<Child> Children = _children.AsReadOnly();
public class Child
{
private Parent _parent;
public Parent Parent
{
get
{
return _parent;
}
set
{
if(value == _parent)
return;
if(_parent != null)
{
_parent._children.Remove(this);
_parent = null;
}
if(value != null)
{
value._children.Add(this);
_parent = value;
}
}
}
}
}
我要问的问题是,是否有任何替代方法可以实现相同的目标,并且比这种方法有更少或更小的缺点?我发现这种方法的主要缺点是:
- 这会导致脚本变大,但使用
partial
会有所帮助。 - 这可能导致比预期更深的嵌套。
- 这会导致冗长的 class 名称。要在
Parent
之外访问Child
,您必须使用Parent.Child
。在 class 名称很长的情况下,尤其是在使用泛型时,这可能会导致代码非常难看。 - 泛型的使用(例如,实现 compile-time 类型安全)会变得混乱。这似乎至少部分源于
Child
是嵌套的并且Parent<T1>.Child
是与Parent<T2>.Child
不同的类型的事实,并且当您希望类型安全是相互的时,这可能会导致非常丑陋使用泛型,或者需要回退到使用 runtime-enforced 类型安全(虽然它可以被封装掉,通常,例如,使用 non-generic 抽象基,其中public
访问器代替protected
).
附带说明一下,这可能是一个很好的例子,说明使用 friend
扩展访问权限可以简化这些问题!
正如我在评论中所述,在父项中添加一个 Remove 方法,这至少不会暴露整个子项列表。类似于:
public class Parent
{
private readonly List<Child> _children = new List<Child>();
public readonly ReadOnlyCollection<Child> Children = _children.AsReadOnly();
public void Remove(Child child)
{
if(child !=null)
{
_children.Remove(child);
}
}
}
当然,如评论和之前的回答所述,您还需要封装将子项添加到父项,这需要 public Add(Child)
方法
我喜欢使用通用的私有接口来公开如下属性:
public class SomeBigOuterClass {
private interface IChildFriend {
void SetParent(Parent parent);
}
public class Parent {
}
public class Child : IChildFriend {
void IChildFriend.SetParent(Parent parent) {
this.parent = parent;
}
private Parent parent;
}
}