Как создать древовидную структуру данных, которая может содержать два универсальных типа различных сетей для родительского и дочернего типов - PullRequest
0 голосов
/ 21 февраля 2019

Мне стыдно признаться, что я сижу над этой проблемой много-много часов.Но я просто хочу реализовать это так, как это структурировано на диаграмме ниже ...

Я хочу смоделировать мир с континентами / странами / государствами и городами.Каждая модель имеет ссылку на своего родителя и список ссылок на своих потомков, за исключением того, что в мире есть только дети (потому что для нее не может быть родителя), а в городе есть только ссылка на родителя, потому что она не идет глубже.(Я хочу реализовать его так, чтобы, например, у «Мира» не было родительского поля, также у «города» нет поля List<Children>.

Я собирался реализовать его в древовидной структуре данных, такой какследующее (я пропустил реализации): Only interfaces shown for simplicity

Чтобы дать вам представление о коде интерфейсов , я включил здесь минимум этого:

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

И небольшой код реализации :

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;

    //...
}

И, наконец, код классов, которые я хочу структурировать :

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

Здесь возникает проблема:

Теперь, чтобы добавить дочерний объект в Root<TChild>.AddChild(TChild) Мне нужен доступ к <TChlid>.Parent, поэтому мне нужно ограничить универсальныйTChild до ILeaf<IRoot<TChild>> примерно так:

public class Root<TChild> : IRoot<TChild> where TChild : ILeaf<Root<TChild>>
{
    public void AddChild(TChild child)
    {
        child.Parent = this;
    }
}

Но при этом я получаю сообщение об ошибке

CS0311 C # Тип нельзя использовать в качестве параметра типа в универсальном типеили метод. Нет неявного преобразования ссылки из в.

В этой строке

public class World : Root<Continent> { }

1 Ответ

0 голосов
/ 23 февраля 2019

Наконец-то я нашел решение.Он состоит из абстрактных базовых классов Root<TChild> и Node<TParent, TChild>.Установка родительского дочернего элемента делегируется абстрактному методу.В конкретных реализациях, где параметры универсального типа были разрешены, доступ к свойству Parent не представляет проблем.

Я также немного изменил интерфейсы.Представление дочерних элементов как List<TChild> проблематично, поскольку позволяет любому обойти логику добавления AddChild путем непосредственного добавления в список и забыть установить родителя дочернего элемента.

Я также сделал Parent свойство доступно только для чтения в интерфейсе, так как метод установки используется только в реализации.

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> { }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...