Generic puzzle

  • Thread starter Thread starter John Rivers
  • Start date Start date
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?
 
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
 
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
 
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
 
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
 
Back
Top