首页 > 解决方案 > 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): Only interfaces shown for simplicity

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

标签: c#genericsarchitecture

解决方案


最后,我找到了解决方案。它包括制作基类Root<TChild>Node<TParent, TChild>抽象类。设置孩子的父母被委托给一个抽象方法。在具体实现中,已经解析了泛型类型参数,那么访问Parent属性就没有问题了。

我也稍微改变了接口。暴露孩子是有问题的,因为它允许任何人通过直接添加到列表List<TChild>来规避添加逻辑并忘记设置孩子的父母。AddChild

我还在Parent接口中将属性设置为只读,因为 setter 仅在实现中使用。

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

基类:

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

请注意,Node它继承自Root,因此我们只需要补充一个ILeaf实现。

具体实现类:

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

推荐阅读