Generic puzzle

J

John Rivers

this seemed like an reasonable thing to do:

class TestBase {
public abstract void Blah<T>(LinkedList<T> list) where T : TestBase;
}//class

class Test : TestBase {
LinkedList<Test> list;
LinkedListNode<Test> node;
public override void Blah<T>(LinkedList<T> list) {
list.AddLast(this);
list.AddLast(node);
}//method
}//class

list.AddLast() has two overloads
one accepts T and one accepts LinkedListNode<T>

these are the compile errors:

//this: Error 164 Argument '1': cannot convert from
'GameSpace.GameOne.Test' to 'T'
//node: Error 164 Argument '1': cannot convert from
'System.Collections.Generic.LinkedListNode<GameSpace.GameOne.Test>' to
'T'

I was getting the hang of generics but I can't get my head around this

Can anybody explain?
 
P

Peter Duniho

John said:
this seemed like an reasonable thing to do:

class TestBase {
public abstract void Blah<T>(LinkedList<T> list) where T : TestBase;
}//class

class Test : TestBase {
LinkedList<Test> list;
LinkedListNode<Test> node;
public override void Blah<T>(LinkedList<T> list) {
list.AddLast(this);
list.AddLast(node);
}//method
}//class

list.AddLast() has two overloads
one accepts T and one accepts LinkedListNode<T>

these are the compile errors:

//this: Error 164 Argument '1': cannot convert from
'GameSpace.GameOne.Test' to 'T'
//node: Error 164 Argument '1': cannot convert from
'System.Collections.Generic.LinkedListNode<GameSpace.GameOne.Test>' to
'T'

I was getting the hang of generics but I can't get my head around this

Can anybody explain?

I'll try. :)

The problem is because your method itself is generic, and thus T could
be _any_ type. Just because your own Test type inherits TestBase, that
doesn't mean that the method is being called using a type for which Test
is compatible.

Consider this code, for example:

class NotTest : TestBase
{
}

void Method(Test test)
{
LinkedList<NotTest> list = new LinkedList<NotTest>();

test.Blah(list);
}

If your previous code had compiled without errors, the above would be
legal at compile time. The call to Blah() meets the constraint given.
But the implementation would try to add a node of type Test to a list
that's supposed to only contain NotTest.

The new version of C# coming out, 4.0, will include some support for
variance in generic types, and that will address some other situations
that are similar, but not quite the same. But this particular scenario
will never be supported, because of the inability of the compiler to
ensure type-safe use of the objects involved.

It's hard to know for sure without more details, but probably this error
is a very good thing, in that it's caught some significant design
problem in your code. Whatever it was that led you to this code
example, you should probably rethink the overall design so that it can
be done in a safe way.

Pete
 
J

John Rivers

Peter thanks for your reply

the workaround I am using at the moment is

abstract class TestBase {
public abstract void Blah(object tlist);
}//class

class Test : TestBase {
LinkedListNode<Test> node;
public override void Blah(object tlist) {
LinkedList<Test> list = (LinkedList<Test>)tlist;
list.AddLast(node);
}//method
}//class

which works but I am not happy with it

the other solution is:

abstract class CrazyGeneric<T> where T : CrazyGeneric<T> {
public abstract void Blah(LinkedList<T> list);
}//class

class Crazy : CrazyGeneric<Crazy> {
LinkedListNode<Crazy> node = null;
public override void Blah(LinkedList<Crazy> list) {
list.AddLast(this);
list.AddLast(node);
}//method
}//class

which is ... crazy ... but it works
 
P

Peter Duniho

John said:
[...]
the other solution is:

abstract class CrazyGeneric<T> where T : CrazyGeneric<T> {
public abstract void Blah(LinkedList<T> list);
}//class

class Crazy : CrazyGeneric<Crazy> {
LinkedListNode<Crazy> node = null;
public override void Blah(LinkedList<Crazy> list) {
list.AddLast(this);
list.AddLast(node);
}//method
}//class

which is ... crazy ... but it works

It's not that crazy. There are other examples, including some in .NET,
where the generic constraint is self-referential. In the right
situation, it's exactly the right way to do it.

If it seems to suit your own design well, then IMHO it's a much better
approach than the first work-around you mention.

Pete
 
J

John Rivers

Now I have thought it through it doesn't seem so crazy
and that approach has worked nicely

I think the reason for my initial post was void as I had simply
forgotten
there was already a perfectly good generic parameter at the class
level
probably the result of a phone call interrupting my concentration

Thanks again for your input
 

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