编写封装父/子数据结构的更简单方法是什么?

时间:2017-02-28 22:05:02

标签: c# hierarchy encapsulation friend member-access

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

  • 父母提到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._childrenprivate。但是,您不希望将其设为私有内容,因为允许在ChildParent之外进行访问会导致违反实施规则或其他地方的规则。

因此,我提出的解决方案是将Child嵌套在Parent中。嵌套类可以访问嵌套在其中的类的私有成员:

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扩展访问权限可以简化这些问题的一个很好的例子!

3 个答案:

答案 0 :(得分: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;
    }
}

答案 1 :(得分:1)

正如我在评论中所说,在父母中添加一个删除方法,这至少不会暴露整个子列表。类似的东西:

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);
            }
        }
    }

答案 2 :(得分:0)

当然,正如评论和之前的回答中所述,您还需要封装将子项添加到父级,这需要公共Option Explicit Sub test() Dim filename As String filename = "myFile2.xlsm" Workbooks.Open filename:="C:\temp\myFile2.xlsm" Workbooks.Open filename:="C:\temp\myFile1.xlsm" Sheets("Sheet1").Copy After:=Workbooks(filename).Sheets(1) End Sub 方法