Easier way to write encapsulated parent/child data structure?

拜拜、爱过 提交于 2019-12-25 09:03:36

问题


From time to time I find myself often writing a data structure of "parents" and "children", where:

  • A parent has references to 0 to N distinct children.
  • A child has a reference to 0 parents or 1 parent.
  • The reference must be mutual. For any given parent, any child that it references must also reference the given parent back. For any given child, the parent that it references must reference the given child back.
  • It's impossible to violate the above rules through use of members accessible from outside the two class declarations (non-private), aside from use of Reflection.

The mental steps one might take before implementing something like this might start with something like this:

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

Of course, this will not compile, since Parent._children is private. But, you wouldn't want to make it anything but private, since allowing access outside of Child or Parent would make it possible to violate the rules in an implementation or elsewhere.

So, a solution I came up with is to nest Child in Parent. Nested classes can access private members of the class its nested within:

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

The question I'm asking is, are there any alternative ways to write this that accomplish the same goals which have fewer or less significant drawbacks than this approach? The main drawbacks I'm finding to this approach are:

  • This can lead to large scripts, though use of partial can help.
  • This can lead to deeper nesting than desired.
  • This can lead to verbose class names. To access Child outside of Parent, you have to use Parent.Child. In cases where the class names are long, and especially when generics are used, this can lead to very ugly code.
  • Use of generics (for example, to achieve compile-time type safety) can get messy. This appears to at least partially stem from the fact that Child is nested and Parent<T1>.Child is a distinct type from Parent<T2>.Child, and when you want type safety to be mutual this can lead to really ugly use of generics, or needing to fall back to using runtime-enforced type safety (though it can be encapsulated away, usually, e.g., using a non-generic abstract base where public accessors are instead protected).

On a side note, this may be a good example of where the use of friend to extend access rights would simplify these problems!


回答1:


I like to use a common, private interface to expose properties like this:

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



回答2:


As I stated in the comment, add a Remove method in the parent and that will at least won't expose the whole children list. Something like:

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



回答3:


And of course, as stated in comments and in previous answer, you will also need to encapsulate adding childs to parent, which would require public Add(Child) method



来源:https://stackoverflow.com/questions/42519598/easier-way-to-write-encapsulated-parent-child-data-structure

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!