c# - 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> { }
解决方案
最后,我找到了解决方案。它包括制作基类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> { }
推荐阅读
- c# - 如何强制填充控制器的 TempData?
- tfs - 包含美元符号的参数字符串
- swift - Auto-Size UITableViewCell 的内部 UITableView
- javascript - Vue数组,获取完整数组作品,数组中的特定项目未定义
- console - 从外部地址访问管理 Web 控制台
- powershell - 从 txt 文件中获取数据并将其导出为 CSV
- asp.net-core - 多个长时间运行的 IHostedSerivces 或 BackgroundService 实现
- ruby - 如何在中间人 ruby 应用程序中使用 cookie?
- reactjs - 一个
可能只有一个子元素。尝试了一切 - mysql - 从 MySQL 中提取所有 JSON 密钥