Passing an inherited class as 'T' to a generic container.

I'm having trouble finding out how to use a general purpose generic container I wrote as a member of another class which wants to hold any class type based on a common base class.

The first set of code below shows 4 seperate classes, RNode is a generic class, TreeControl has an RNode<TreeItemBase> member, and a "Root" property to access it, TreeItemBase is a control (not really important to this), and TreeItemHeader is derived from TreeIItemBase...

Public class RNode<T> {}

public partial class TreeControl : ScrollableControl

{

private RNode<TreeItemBase> root = null;

public RNode<TreeItemBase> Root

{

get { return root; }

set { root = value; }

}

}

public partial class TreeItemBase : UserControl {}

public partial class TreeItemHeader : TreeItemBase {};

The next set of code is in a class that is testing the usage of these other classes...

TreeItemBase item = new TreeItemHeader();

TreeItemHeader header = new TreeItemHeader();

RNode<TreeItemHeader> root = new RNode<TreeItemHeader>(header);

this.treeControl1.Root = root;

Note the first line "TreeItemBase item = new TreeItemHeader(); compiles just fine (ahhh... gotta love inheritance.)

My question: Why doesn't the last line "this.treeControl1.Root = root;" give me an error that I can not implicitly convert the TreeItemHeader to TreeItemBase here I dont' want to have to cast each one of these... this was the whole reason for building a generic class in the first place.. so I could use RNode<T> in a variety of places, and mantain type safety.

Now I have not tried, but assume that if I defined the RNode<T> with a "where T : TreeItemBase" that this might work, but again, I intend on using the RNode class in a variety of places. I'm new to C#, and hoping I'm missing something I can do to fix this.

Live long and program,

Jim Tomasko



Answer this question

Passing an inherited class as 'T' to a generic container.

  • johnof

    Thanks Figo,

    Actually, I think that maybe "TreeItemHeader" might have been a bit misleading of a type to use with such a small example... you really shouldn't take any meaning from it's type, but just that it was derived from TreeItemBase. I want to have a variety of classes all based upon TreeItemBase, but I guess that's not going to be possible without a lot of intermediate casting.

    I guess the best thing I can do right now is to learn more about the guts of C#. What topic(s) would I be looking at to get a better understanding of why RNode<BaseObject> = RNode<DerivedFromBaseObject> will not work, when BaseObject = DerivedFromBaseObject will work. Maybe this will shead some light on the problem.

    Thanks,

    Jim Tomasko


  • Fusion54

    Hi,

    About generic class, if the type in the<> is not the same, even though what else are exactly the same, those classes are not able to be evaluated by others. Generic class just give us more feasible way to define different types in common.

    Though I'm not sure what exact you want it to do. However, why do you consider pass directly value between two different nodes

    If you want to restrict the types in the Rnode<>, pls consider that use:

    Public class RNode<T> where T: BaseObject

    {}

    Thanks



  • Mark Hsieh

    Hi,

    In this case, what about define root in class TreeControl as RNode<TreeItemHeader> instead of RNode<TreeItemBase>, since root is a header.

    Otherwise, what use of TreeItemHeader

    Thanks



  • Bharat Bhushan

    You make the (very common) assumption that RNode<TreeItemBase> and RNode<TreeItemHeader> are somehow related because TreeItemHeader derives from TreeItemBase. But in fact they are not, the two RNode types are completely different types and you can't assign one to the other.

    As for workarounds, can't you change

    RNode<TreeItemHeader> root = new RNode<TreeItemHeader>(header);

    to

    RNode<TreeItemBase> root = new RNode<TreeItemBase>(header);



  • schalti

    Hi, Jim

    It is about generics class concept in C#.

    If we define two class say:

    Class Node<>{}

    Node<string> class does not have inheritance relation with Node<object> class, although string is derived from object.

    Thanks



  • Steve Jensen

    While this workaround would work, it would make adding new nodes a fairly combersome process, with an extra construction just to get around this problem. Maybe I need to get rid of the genric type safety and come up with a differant solution on how to implement this tree, or maybe I need to reinvent the tree each time I need to use it.

    The intent of the tree is to act as a "scene graph". While there will be a common base class to any given tree, there will be a variety of data types that are valid as node entries. Picture the often used "graphic object" example, where you have a circle class, a square class and triangle class, each one inheriting a virtual draw method (or interface).

    In the above case, I would want to be able to be able to make a container to hold types of the base class, but pass it derived types. Is there any other method of construction of a container where I can pass in derived objects without type casting each one

    P.S... While I don't want to turn this thread into another topic, someone marked the first two responces as "the answer", which I have turned off. Please leave that for the poster of the question, as they are the only one that knows if the intent of the question was answered. (Although both replies so far have been informative and welcome.)

    Thanks,

    Jim Tomasko


  • Drew Marsh

    Thanks again Figo,

    The BaseObject tip is a great one, and I have added it to my generic RNode class. This will make the generic class I've got be safer for it's intended use.

    Unfortunately, it did not solve the casting problem I had with my other class. I did solve the problem by doing two things:

    1. Directly implementing tree traversal functions and a reference to the root inside of my TreeControl class.

    2. Adding properties for parent, child, and sibling directly to my TreeItemBase class.

    Now I can inherit from my base TreeItemBase class, and avoid all the nasty casting as I add new nodes to the tree.

    This does everything I want it to with a simple syntax that's easy to follow for customers of the TreeControl class. The only place where I think I failed is having to reimplement the functionality of my generic tree inside the TreeControl class to avoid the extra casting. Seems to me that the generics have a basic flaw in their design if inherited classes need to be explicitly cast when used as type 'T'.

    Thank you for your input, (and to all that answered). While the answer ended up "you can't have your cake and eat it too", it would have taken me quite a while to prove that on my own.

    I'm going back and marking the first two original replies as the answer... although the real answer was to rethink the architechure, the answers were right in the sence that my architecture was flawed.

    Jim Tomasko


  • Passing an inherited class as 'T' to a generic container.