Дженерика и Родительская / Детская архитектура - PullRequest
7 голосов
/ 22 марта 2011

Я строю архитектуру с наследуемыми обобщениями и отношениями между родителями и детьми.У меня есть одна большая проблема: я не могу сделать так, чтобы и ребенок, и родитель знали о типе друг друга, только один из двух.

Мне нужно, чтобы и ребенок, и родитель знали о типе друг друга.

Сценарий 1 : Родитель знает тип потомка, но ребенок знает только родового родителя с родовыми потомками.

public class Child
{
    public Parent<Child> Parent;
}

public class Parent<TChild>
    where TChild : Child
{
    public List<TChild> Children;
}

Сценарий 2 : ребенокзнает родительский тип, но родитель знает только общих детей с общим родителем.

public class Child<TParent>
    where TParent : Parent
{
    public TParent Parent;
}

public class Parent
{
    public List<Child<Parent>> Children;
}

Сценарий 3 : Утопический, но недостижимый сценарий:

public class Child<TParent>
    where TParent : Parent
{
    public TParent Parent;
}

public class Parent<TChild>
    where TChild : Child
{
    public List<TChild> Children;
}

Конечно,сценарий 3 не будет компилироваться, потому что Parent и Child используют второй универсальный тип, который будет их собственным типом, но я не могу (или, по крайней мере, не знаю, как!) указать, что это ихсобственный тип.

Я попадаю в какую-то бесконечную петлю / рекурсию / бросок мяча здесь, пожалуйста, помогите мне, прежде чем я утону.

Удачи.

РЕДАКТИРОВАТЬ : Если я не был понятен, я могу сослаться на свои типы да, но если пользователь выводит Childв FooChild и доступ к дочерним элементам родителя FooChild, это будет только Child, а не FooChild, как и должно быть.

Пример сбоя со сценарием 1 :

public class Child
{
    public Parent<Child> Parent;
}

public class Parent<TChild>
    where TChild : Child, new()
{
    public List<TChild> Children;
}

public class FooChild : Child
{
    public int Required;

    public void Bar()
    {
        foreach (Child child in this.Parent.Children)
        {
            int x = child.Required; // cannot be accessed!
        }
    }
}

Пример сбоя со сценарием 2 :

public class Child<TParent>
    where TParent : Parent
{
    public TParent Parent;
}

public class Parent
{
    public List<Child<Parent>> Children;
}

public class FooChild : Child<Parent>
{
    public int Required;

    public void Bar()
    {
        foreach (Child<Parent> child in this.Parent.Children)
        {
            int x = child.Required; // cannot be accessed!
        }
    }
}

1 Ответ

17 голосов
/ 22 марта 2011

Вы, конечно, можете делать то, что просите.Определите ваши классы следующим образом:

public abstract class Child<P, C>
    where P : Parent<P, C>
    where C : Child<P, C>
{
    public P Parent { get; set; }
}

public abstract class Parent<P, C>
    where P : Parent<P, C>
    where C : Child<P, C>
{
    public List<C> Children = new List<C>();
}

Затем вы можете определить свой конкретный класс следующим образом:

public class Cat : Parent<Cat, Kitten>
{
}

public class Kitten : Child<Cat, Kitten>
{
}

public class Dog : Parent<Dog, Puppy>
{
}

public class Puppy : Child<Dog, Puppy>
{
}

Теперь у вас есть полностью типизированные отношения родитель-потомок.Этот код теперь работает:

var fluffy = new Cat();
var fluffette1 = new Kitten() { Parent = fluffy };
var fluffette2 = new Kitten() { Parent = fluffy };

fluffy.Children.Add(fluffette1);
fluffy.Children.Add(fluffette2);

var butch = new Dog();
var butchette1 = new Puppy() { Parent = butch };
var butchette2 = new Puppy() { Parent = butch };

butch.Children.Add(butchette1);
butch.Children.Add(butchette2);

Надеюсь, это поможет.

...