0

有时我发现自己经常写一个“父母”和“孩子”的数据结构,其中:

  • 一个父级引用了 0 到 N 个不同的子级。
  • 一个孩子有 0 个父母或 1 个父母的参考。
  • 引用必须是相互的。对于任何给定的父母,它引用的任何孩子也必须引用给定的父母。对于任何给定的孩子,它引用的父母必须引用给定的孩子。
  • 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._childrenis private. 但是,除了私有之外,您不想让它成为任何东西,因为允许外部访问ChildParent可能会违反实现或其他地方的规则。

所以,我想出的一个解决方案是嵌套ChildParent. 嵌套类可以访问其嵌套的类的私有成员:

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可以提供帮助。
  • 这可能导致比预期更深的嵌套。
  • 这可能导致冗长的类名。要访问Child外部Parent,您必须使用Parent.Child. 在类名很长的情况下,尤其是在使用泛型时,这可能会导致代码非常难看。
  • 使用泛型(例如,实现编译时类型安全)可能会变得混乱。这似乎至少部分源于Child嵌套Parent<T1>.Child的事实,Parent<T2>.Child并且与 .安全性(尽管它可以被封装掉,通常,例如,使用public访问器所在的非泛型抽象基protected)。

附带说明一下,这可能是一个很好的例子,说明使用friend扩展访问权限可以简化这些问题!

4

3 回答 3

2

我喜欢使用通用的私有接口来公开如下属性:

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;
    }
}
于 2017-02-28T22:17:02.887 回答
1

正如我在评论中所说,在父级中添加一个 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);
            }
        }
    }
于 2017-02-28T22:14:52.027 回答
0

当然,正如评论和之前的回答中所述,您还需要封装将子代添加到父代,这需要公共Add(Child)方法

于 2017-02-28T22:16:16.130 回答