Suppose you had a data structure consisiting of "Parents" and "Children", where references between parents and children were mutual:
Borrowing from a previous post, the following code is written to guarantee mutual referencing between parents and children (short of abuse of Reflection):
public static class Base
{
private interface IParent
{
List<Child> Children { get; }
}
public class Parent : IParent
{
private readonly List<Child> _children = new List<Child>();
public readonly ReadOnlyCollection<Child> Children = _children.AsReadOnly();
List<Child> IParent.Children { get { return _children; } }
}
public class Child
{
private Parent _parent;
public Parent Parent
{
get
{
return _parent;
}
set
{
if(value == _parent)
return;
if(_parent != null)
{
((IParent)_parent).Children.Remove(this);
_parent = null;
}
if(value != null)
{
((IParent)value).Children.Add(this);
_parent = value;
}
}
}
}
}
Now suppose you wanted a similar structure, but you also wanted type safety. That being, instances of TParent
could only reference instances of TChild
and instances of TChild
could only reference instances of TParent
.
I came up with this solution:
public static class Base<TParent, TChild>
where TParent : Base<TParent, TChild>.Parent
where TChild : Base<TParent, TChild>.Child
{
private interface IParent
{
List<TChild> Children { get; }
}
public class Parent : IParent
{
private readonly List<TChild> _children = new List<Child>();
public readonly ReadOnlyCollection<TChild> Children = _children.AsReadOnly();
List<TChild> IParent.Children { get { return _children; } }
}
public class Child
{
private TParent _parent;
public TParent Parent
{
get
{
return _parent;
}
set
{
if(value == _parent)
return;
if(_parent != null)
{
// Oh no, casting!
((IParent)_parent).Children.Remove((TChild)this);
_parent = null;
}
if(value != null)
{
// Oh no, casting!
((IParent)value).Children.Add((TChild)this);
_parent = value;
}
}
}
}
}
And while this works, the points where Child
is casted to TChild
inside Child.Parent.set
worry me a bit. Though I'm not sure there'd be a way to use this class that throws an InvalidCastException
, it may still be impossible to break.
Is there a cleaner way to achieve this effect?