I am ashamed to admit that I am sitting on this problem for many many hours. But I just want to implement it the way I have it structured in the diagramm below....
I want to model the world with continents/countrys/states and citys in it. Each model has a reference to its parent and a list of references to its children except the world only has children (because there cant be a parent to it) and the city only has a parent-reference because it doesnt go deeper. (I want to implement it that for example "World" does not have a parent field, likewise "city" does not have a List<Children>
field.
I was about to implement it in a tree data structure like following (I left out the implementations):
To give you an idea of the code of the interfaces I included the bare minimum of it here:
public interface IRoot<TChild>
{
List<TChild> Children { get; set; }
void AddChild(TChild child);
}
public interface ILeaf<TParent>
{
TParent Parent { get; set; }
}
public interface INode<TParent, TChild> : IRoot<TChild>, ILeaf<TParent> { }
And a little code of the implementation :
public class Root<TChild> : IRoot<TChild>
{
public List<TChild> Children { get; set; }
public void AddChild(TChild child) { //... }
}
public class Leaf<TParent> : ILeaf<TParent>
{
public TParent Parent { get; set; }
}
public class Node<TParent, TChild> : INode<TParent, TChild>
{
private IRoot<TChild> root;
private ILeaf<TParent> leaf;
//...
}
Lastly the code of the classes I want to structure :
public class World : Root<Continent> { }
public class Continent : Node<World, Country> { }
public class Country : Node<Continent, State> { }
public class State : Node<Country, City> { }
public class City : Leaf<City> { }
Here comes the Problem:
Now to Add a child object in Root<TChild>.AddChild(TChild)
I need to access <TChlid>.Parent
so I would need to constraint the generic TChild
to ILeaf<IRoot<TChild>>
like this:
public class Root<TChild> : IRoot<TChild> where TChild : ILeaf<Root<TChild>>
{
public void AddChild(TChild child)
{
child.Parent = this;
}
}
But doing this, I get the Error
CS0311 C# The type cannot be used as type parameter in the generic type or method. There is no implicit reference conversion from to.
At this line
public class World : Root<Continent> { }
Finally, I have found a solution. It consists of making the base classes Root<TChild>
and Node<TParent, TChild>
abstract. Setting the parent of the child is delegated to an abstract method. In the concrete implementations, where the generic type parameters have been resolved, it is then no problem to access the Parent
property.
I have also changed the interfaces slightly. Exposing the children as List<TChild>
is problematic, as it allows anybody to circumvent the adding logic of AddChild
by directly adding to the list and to forget to set the child's parent.
I also made the Parent
property read-only in the interface, as the setter is only used in the implementation.
public interface IRoot<TChild>
{
IReadOnlyList<TChild> Children { get; }
void AddChild(TChild child);
}
public interface ILeaf<TParent>
{
TParent Parent { get; }
}
public interface INode<TParent, TChild> : IRoot<TChild>, ILeaf<TParent>
{
}
The base classes:
public abstract class Root<TChild> : IRoot<TChild>
{
private List<TChild> _children = new List<TChild>();
public IReadOnlyList<TChild> Children => _children;
public void AddChild(TChild child)
{
_children.Add(child);
SetChildsParent(child);
}
protected abstract void SetChildsParent(TChild child);
}
public class Leaf<TParent> : ILeaf<TParent>
{
public TParent Parent { get; internal set; }
}
public abstract class Node<TParent, TChild> : Root<TChild>, INode<TParent, TChild>
{
public TParent Parent { get; internal set; }
}
Note that Node
is inheriting from Root
, thus we need only to supplement an ILeaf
implementation.
The concrete implementation classes:
public class World : Root<Continent>
{
protected override void SetChildsParent(Continent child) => child.Parent = this;
}
public class Continent : Node<World, Country>
{
protected override void SetChildsParent(Country child) => child.Parent = this;
}
public class Country : Node<Continent, State>
{
protected override void SetChildsParent(State child) => child.Parent = this;
}
public class State : Node<Country, City>
{
protected override void SetChildsParent(City child) => child.Parent = this;
}
public class City : Leaf<State> { }
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.