Родительский родовой дочерний C # - PullRequest
6 голосов
/ 09 марта 2011

У меня есть родительский класс Container, который может содержать любой тип Node, где Node является подклассом универсального класса, специфичного для родителя, например:

public class ContainerBase<NodeType, ObjectType>
    where NodeType : NodeBase<ObjectType> where ObjectType : ObjectBase {
}

public abstract class NodeBase<T> where T : ObjectBase {
    ContainerBase<NodeBase<T>, T> container;
    public NodeBase(ContainerBase<NodeBase<T>, T> owner) {
        container = owner;
    }
}

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

public class ContainerNormal : ContainerBase<NodeNormal, ObjectNormal> {
}

public class NodeNormal : NodeBase<ObjectNormal> {
    //This doesn't work
    public NodeNormal(ContainerNormal owner) : base(owner) { }
}

Я немного понимаю, почему вызов базового конструктора не работает. Он пытается преобразовать ContainerNormal в ContainerBase<NodeBase<ObjectNormal>, ObjectNormal>, который на самом деле не работает.

Так какой шаблон дизайна мне не хватает для правильной работы? Или мне просто нужно взять ContainerBase<NodeBase<ObjectNormal>,ObjectNormal> в конструкторе, даже если это не обязательно объект ContainerNormal?

Ответы [ 4 ]

3 голосов
/ 09 марта 2011

Исследуйте: Ковариация и контравариантность , но это должно работать:

public class Container<TNode, T> where TNode : Node<T> { }

public abstract class Node<T>
{
    Container<Node<T>, T> container;

    public Node(Container<Node<T>, T> owner)
    {
        this.container = owner;
    }
}

public class ContainerNormal<T> : Container<Node<T>, T> { }

public class NodeNormal<T> : Node<T>
{
    public NodeNormal(ContainerNormal<T> container)
        : base(container)
    {
    }
}

public class ContainerNormal : ContainerNormal<string> { }

public class NodeNormal : NodeNormal<string>
{
    public NodeNormal(ContainerNormal container)
        : base(container)
    {
    }
}
1 голос
/ 11 марта 2011

У тебя есть одна хитрость, которая, я думаю, получит то, что ты хочешь.Вы можете дать NodeBase дополнительный универсальный параметр, который должен наследоваться от NodeBase.Это дает определение типа, которое кажется рекурсивным, но у компилятора есть способ, которым он это решает.Примерно так должно работать:

public class NodeBase<T, TNode> :
    where T : ObjectBase
    where TNode : NodeBase<T, TNode>
{ 
    private ContainerBase<TNode, T> container;

    protected NodeBase(ContainerBase<TNode, T> owner)
    {  container = owner; }
}

public class ContainerBase<NodeType, ObjectType> :
    where NodeType : NodeBase<ObjectType, NodeType>
    where ObjectType : ObjectBase
{
    public NodeType GetItem() { ... }
}

public class NodeNormal : NodeBase<ObjectNormal, NodeNormal>
{
    public NodeNormal(ContainerNormal owner) :
        base(owner) { }
}

public class ContainerNormal : 
    ContainerBase<NodeNormal, ObjectNormal>
{ 
    //GetItem would return NodeNormal here
}
1 голос
/ 09 марта 2011

В C # 4 это можно сделать с помощью универсальной ковариации интерфейса:

public class ContainerBase<NodeType, ObjectType> : IContainerBase<NodeType, ObjectType>
    where NodeType : NodeBase<ObjectType> where ObjectType : ObjectBase {
}

public abstract class NodeBase<T> where T : ObjectBase {
    IContainerBase<NodeBase<T>, T> container;
    public NodeBase(IContainerBase<NodeBase<T>, T> owner) {
        container = owner;
    }
}

public class ContainerNormal : ContainerBase<NodeNormal, ObjectNormal> {
}

public interface IContainerBase<out NodeType, ObjectType>
    where NodeType : NodeBase<ObjectType> where ObjectType : ObjectBase {
}

public class NodeNormal : NodeBase<ObjectNormal> {
    //This doesn't work
    public NodeNormal(ContainerNormal owner) : base(owner) { }
}

public class ObjectNormal : ObjectBase {}

public class ObjectBase{}

Конечно, это будет работать, только если ваш интерфейс IContainerBase может избежать использования каких-либо функций, которые бы принимали NodeType в качестве ввода. Например, это будет работать:

public interface IContainerBase<out NodeType, ObjectType>
    where NodeType : NodeBase<ObjectType> where ObjectType : ObjectBase 
{
    NodeType NodeTypeProp {get;}
}

... но это не так:

public interface IContainerBase<out NodeType, ObjectType>
    where NodeType : NodeBase<ObjectType> where ObjectType : ObjectBase 
{
    NodeType NodeTypeProp {get;set;} // "set" not allowed on "out" type
}

Еще одно замечание: вы заметили, как мне пришлось называть свойство «NodeTypeProp» вместо «NodeType»? Это потому, что мы не следуем соглашениям об именах C #. Вы должны использовать префикс "T" для имен обобщенных типов:

public interface IContainerBase<out TNodeType, TObjectType>
    where TNodeType : NodeBase<TObjectType> where TObjectType : ObjectBase 
0 голосов
/ 09 марта 2011

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

public class ContainerNormal : ContainerBase<NodeBase<ObjectNormal>, ObjectNormal>
{
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...