简体   繁体   中英

How do i make a tree data structure that can hold two differnet generic types for parent and children

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM