From time to time I find myself often writing a data structure of "parents" and "children", where:
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:
partial
can help.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.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!
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;
}
}