Birth parent of a birth child C #

I have a parent class container that can contain any kind of Node, where Node is a subclass of a generic class specific to the parent, for example:

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

I want to create specific subclasses for simplicity that implement standard object types:

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

I understand somewhat why calling the base constructor does not work. It tries to convert a ContainerNormal to ContainerBase<NodeBase<ObjectNormal>, ObjectNormal> , which really does not work.

So, what design template am I missing to make this work right? Or do I just need to take ContainerBase<NodeBase<ObjectNormal>,ObjectNormal> in the constructor, although it may not necessarily be a ContainerNormal object?

+6
generics c # design-patterns
source share
4 answers

Explore: Covariance and contravariance , but this should work:

 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) { } } 
+3
source share

In C # 4, you can accomplish this using common interface covariance:

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

Of course, this will only work if your IContainerBase interface can avoid using any functions that will accept NodeType input. For example, this will work:

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

... but it will not:

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

One more note: did you notice how I needed to specify the property “NodeTypeProp” instead of “NodeType”? This is because we do not follow C # naming conventions. You need prefix type names of type "T":

 public interface IContainerBase<out TNodeType, TObjectType> where TNodeType : NodeBase<TObjectType> where TObjectType : ObjectBase 
+1
source share

There is one trick you can do, I think you will get what you want. You can provide NodeBase with an additional generic parameter that should inherit from NodeBase. This gives a type definition that appears recursive, but the compiler has a way to fix it. Something like this should work:

 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
source share

You can modify the definition of Container normal as follows:

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

All Articles