C#/2.0: Tricky issue with generics.

T

Thomas Tomiczek

Ok, working my way through a complex library conversion to .NET 2.0 and C#
2.0 with generics I am totally stuck on one thing -if anyone sees the issue
I would be more than glad.

The situation is that we have a tree of "Node" objects. A Node is a
visualizable item, like a component. THe menues of all or ouf applications,
for example, are node hierarchies that an adapter transposes into a menu
model.

Now, Nodes have an internal DataObject, which can hold the actual data -
often a business object. This was so far a Object, now it will be T.

So, the definition of Node looks like this now:

public class Node<T> :
SmartObject,
INamed
where
T : class
{

The T is supposed to indicate the type of the DataObject, which can be any
class. There is a class NodeList that is basically a glorified IList with a
lot of additional functionality. It looks like this:

public class NodeList<T> :
ObjectBindingList<T>
where
T : Node<Object>
{

The idea is that a NodeList can hold any Node and should be strong-typable
for this. Now, here the issues come. A Node itself has a Collection
"Children". This is similar to the collection if children in the component
hierarchy. Nodes are not restricted to subtypes - and the constructor of the
NodeList can take a Node that is the parent. If there is a parent in a
NodeList, there is a lot of stuff going on when you add/remove thigs. So,
there is aconstructor like this:

public NodeList(Node<Object> Parent)

As you can see, ANY Node can be the parent.

And here the issues start. In the constructor of the Node I create the
ChildNodes:

_ChildNodes = new NodeList<Node<Object>> (this);

And the issue is: this blows. And I have no clue why. The error messages
are:

Error 2 The best overloaded method match for
'Foundation.Nodes.NodeList<Foundation.Nodes.Node<object>>.NodeList(Foundation.Nodes.Node<object>)'
has some invalid arguments
C:\WORK\ThonaConsulting\EntityBroker\03.0\Foundation\Nodes\Node.cs 29 18
Foundation

Error 3 Argument '1': cannot convert from 'Foundation.Nodes.Node<T>' to
'Foundation.Nodes.Node<object>'
C:\WORK\ThonaConsulting\EntityBroker\03.0\Foundation\Nodes\Node.cs 29 46
Foundation
Now, this I simply do not understand. The argument "this" is a Node<T> - and
is not convertible to Node<object>? Why not? T is supposed to be a class,
and object is the base-class of all classes. Also, I cano not even cast
this:

_ChildNodes = new NodeList<Node<Object>> ((Node<T>)this);

produces the exactly SAME error. I simply do not understand why. As I said,
the case should be totally valid in my optinion.

Anyone a clue? This starts to look like a compiler error to me, but I still
fear I just overlook something. I mean, this is created WITHIN A node, so
thi sshould be totally valid, or?

Thomas Tomiczek
 
N

Nicholas Paldino [.NET/C# MVP]

Thomas,

The reason for this is that with generics, if you have a class B, that
inherits from A, then C<B> does NOT inherit from C<A>. The same goes for
casting.

To get around this, you should have a non-generic interface which
exposes the functionality of your node, and pass that around to the
constructor of your node class.

Hope this helps.
 
B

Bruce Wood

Nicholas,

I've been curious about this problem although I haven't moved to C# 2.0
yet.

An interface is a good idea. Could you not also create a non-qualified
Node type and inherit all Node<T> types from that? Then you could say
simply "Node" with no qualification to indicate any type of Node<T>.
Would that also work?
 
N

Nicholas Paldino [.NET/C# MVP]

Bruce,

That would be fine as well. Either way would work. However, it the
type definition gets messy that way because you have one property returning
the value as object (on the base), and another returning the type-safe value
(on the derived generic class). With the interface, you don't clutter up
the public face of the type as much.
 
B

Bruce Wood

Good point. In order to resolve that problem you would have to make the
non-generic base class Node into a runt class with only those methods
and properties that did not differentiate by the argument type in the
generic version. Given that, it would practically become an interface
anyway.

So, yes, I can see that an interface is a better way to solve the
problem.
 
T

Thomas Tomiczek

Whow. What a ****up.

So, if A is Subclass of B

then c<A> is NOT a subclass of c<B>?

This makes a lot of things way more complicated.

Is there a part of the documentation describing this?

It looks like I HAVE to go with this interface thing - personally I must say
I hate this.

Thomas

Nicholas Paldino said:
Thomas,

The reason for this is that with generics, if you have a class B, that
inherits from A, then C<B> does NOT inherit from C<A>. The same goes for
casting.

To get around this, you should have a non-generic interface which
exposes the functionality of your node, and pass that around to the
constructor of your node class.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)


Thomas Tomiczek said:
Ok, working my way through a complex library conversion to .NET 2.0 and
C# 2.0 with generics I am totally stuck on one thing -if anyone sees the
issue I would be more than glad.

The situation is that we have a tree of "Node" objects. A Node is a
visualizable item, like a component. THe menues of all or ouf
applications, for example, are node hierarchies that an adapter
transposes into a menu model.

Now, Nodes have an internal DataObject, which can hold the actual data -
often a business object. This was so far a Object, now it will be T.

So, the definition of Node looks like this now:

public class Node<T> :
SmartObject,
INamed
where
T : class
{

The T is supposed to indicate the type of the DataObject, which can be
any class. There is a class NodeList that is basically a glorified IList
with a lot of additional functionality. It looks like this:

public class NodeList<T> :
ObjectBindingList<T>
where
T : Node<Object>
{

The idea is that a NodeList can hold any Node and should be
strong-typable for this. Now, here the issues come. A Node itself has a
Collection "Children". This is similar to the collection if children in
the component hierarchy. Nodes are not restricted to subtypes - and the
constructor of the NodeList can take a Node that is the parent. If there
is a parent in a NodeList, there is a lot of stuff going on when you
add/remove thigs. So, there is aconstructor like this:

public NodeList(Node<Object> Parent)

As you can see, ANY Node can be the parent.

And here the issues start. In the constructor of the Node I create the
ChildNodes:

_ChildNodes = new NodeList<Node<Object>> (this);

And the issue is: this blows. And I have no clue why. The error messages
are:

Error 2 The best overloaded method match for
'Foundation.Nodes.NodeList<Foundation.Nodes.Node<object>>.NodeList(Foundation.Nodes.Node<object>)'
has some invalid arguments
C:\WORK\ThonaConsulting\EntityBroker\03.0\Foundation\Nodes\Node.cs 29 18
Foundation

Error 3 Argument '1': cannot convert from 'Foundation.Nodes.Node<T>' to
'Foundation.Nodes.Node<object>'
C:\WORK\ThonaConsulting\EntityBroker\03.0\Foundation\Nodes\Node.cs 29 46
Foundation
Now, this I simply do not understand. The argument "this" is a Node<T> -
and is not convertible to Node<object>? Why not? T is supposed to be a
class, and object is the base-class of all classes. Also, I cano not even
cast this:

_ChildNodes = new NodeList<Node<Object>> ((Node<T>)this);

produces the exactly SAME error. I simply do not understand why. As I
said, the case should be totally valid in my optinion.

Anyone a clue? This starts to look like a compiler error to me, but I
still fear I just overlook something. I mean, this is created WITHIN A
node, so thi sshould be totally valid, or?

Thomas Tomiczek
 
B

Bruce Wood

No, no. It's not a ****-up by any means. It's the only way that
generics can logically work. Think of what would happen if, as you
said, A being a subclass of B would make c<A> a subclass of c<B>. Take
List<T> for example.

The whole point of List<T> is to provide a type-safe list that
guarantees that all of the things in List<Car>, for example, are Cars.
If List<Car> were a subclass of List<Object> then List<Car> would
export of all its methods (such as .Add(Car T)) as well as all of the
methods of List<Object> (such as .Add(Object T)).

Just to make that clear, it would be (conceptually) something like
this:

public class List<Object>
{
public void Add(Object T) { ... }
}

public class List<Car> : List<Object>
{
public void Add(Car T) { ... }
}

Of course, that's junk code... it's just to give you the conceptual
idea. So what would this do? It would mean that List<Car> would export
_two_ Add methods. One that adds a Car to the list, another that adds
an Object to the list (via the base method in List<Object>). So now we
just lost type safety in the list! Now you can add any object you like
to the list, and you can no longer guarantee that the list will always
contain Cars, so you just lost all of the benefit of generics: you
might as well go back to plain old List!

Each type that you build from a generic has no inheritance relationship
to the other types built from that same generic unless you specifically
indicate one. I know it seems illogical at first glance, but
automatically creating such relationships would render generics useless.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top