2011-03-09 9 views
6

Tengo un contenedor clase padre que puede contener cualquier tipo de nodo, donde nodo es una subclase de una clase genérica específica a los padres, así:matriz genérica de niño genérico C#

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

Lo quiero hacer es crear subclases concretas para simplificar, que implementan tipos de objetos estándar:

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

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

que un poco entender por qué la llamada al constructor base no funciona. Está intentando convertir un ContainerNormal a un ContainerBase<NodeBase<ObjectNormal>, ObjectNormal> que realmente no funciona.

Entonces, ¿qué patrón de diseño me falta para que esto funcione, verdad? ¿O simplemente tengo que tomar un ContainerBase<NodeBase<ObjectNormal>,ObjectNormal> en el constructor, aunque no necesariamente sea un objeto ContainerNormal?

Respuesta

3

Explorar: Covariance and contravariance, pero esto debería funcionar:

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) 
    { 
    } 
} 
+0

Parece que realmente puede funcionar, aunque acabará saturando el espacio de nombres un poco ... –

+0

Después de la inspección, parece tener el mismo problema que la respuesta de Can Gencer, en ese intento de obtener un NodeNormal de un ContainerNormal en su lugar devuelve un Nodo , que es casi lo mismo, pero no del todo. –

1

En C# 4 se puede lograr esto mediante la interfaz genérica de covarianza:

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

Por supuesto, esto sólo funcionará si su interfaz IContainerBase puede no tener ninguna función que tendrían un NodeType como entrada. Por ejemplo, esto funcionaría:

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

... pero esto no lo haría:

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

Una nota más: Usted cuenta de lo que tenía que nombrar la propiedad "NodeTypeProp" en lugar de "NodeType "¿?" Eso es porque no estamos siguiendo las convenciones de nomenclatura de C#. Usted debe prefijo a los nombres de tipo genérico con "T":

public interface IContainerBase<out TNodeType, TObjectType> 
    where TNodeType : NodeBase<TObjectType> where TObjectType : ObjectBase 
+0

Por desgracia, estoy atascado en .NET 3.5, de lo contrario esto suena como la solución más limpia. –

+0

@ Ed Marty: Es una pena. Lo que he tenido que hacer en el pasado para cosas como estas es juntar todas estas clases juntas. Por lo tanto, los argumentos genéricos de NodeBase incluirían el tipo ContainerBase y los argumentos genéricos de ContainerBase incluirían el tipo NodeBase. Es feo – StriplingWarrior

0

Puede puede cambiar la definición de contenedor normal de la siguiente manera:

public class ContainerNormal : ContainerBase<NodeBase<ObjectNormal>, ObjectNormal> 
{ 
} 
+0

Eso solucionaría el error de compilación, pero no resuelve el problema de que, por ejemplo, los niños que no sean NodeNormals pueden almacenarse en ContainerNormals. Y si trato de sacar a un niño de un ContainerNormal, será de tipo NodeBase en lugar de NodeNormal. Entonces, si quisiera agregar algún método a NodeNormal y llamarlo a un objeto que obtuve de un ContainerNormal, no funcionaría sin el casting. –

+0

Sí, en ese caso necesitaría la clase intermedia que hereda de ContainerBase , ObjectNormal> como un ContainerNormal : ContainerBase , ObjectType>. Creo que se complicará y de repente tendrás que lidiar con dos tipos de ContainerNormals.Puede agregar una propiedad a ContainerNormal para obtener ObjectNormal directamente y agregar un método para validar que siempre obtiene ObjectNormals. Un poco desordenado, pero creo que será más fácil usar API sabio. –

+0

No debería ser un problema si no planea implementar más de un tipo que heredará de NodeBase . –

1

Hay un truco que puedes hacer que creo que obtendría lo que deseas. Podría darle a NodeBase un parámetro genérico adicional que debe heredar de NodeBase. Esto da una definición de tipo que parece recursiva, pero el compilador tiene una forma de resolverlo. Algo como esto debería funcionar:

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 
} 
Cuestiones relacionadas