.NET 2.0 Generics and type casting

K

Kris Jennings

Hi,

I am trying to create a new generic class and am having trouble casting
a generic type to a specific type. For example,

public class MyClass<T> where T : MyItemClass, new()
{
public MyClass() { }

public void AppendItem()
{
T myItem = new T();
myItem.Parent = this;
}

}

public class MyItemClass
{
public MyClass<MyItemClass> Parent;

public MyItemClass() { }
}

fails to compile with the error: Cannot implicitly convert type
'GenericInheritanceTst.MyClass<T>'
to 'GenericInheritanceTst.MyClass<GenericInheritanceTst.MyItem Class>'.

Has anyone seen this problem and found a resolution?

Thanks.


Kris
 
B

Barry Kelly

Kris Jennings said:
I am trying to create a new generic class and am having trouble casting
a generic type to a specific type. For example,

public class MyClass<T> where T : MyItemClass, new()
{
public MyClass() { }

public void AppendItem()
{
T myItem = new T();
myItem.Parent = this;
}
}

public class MyItemClass
{
public MyClass<MyItemClass> Parent;

public MyItemClass() { }
}

The root of the problem is that C# generics aren't covariant.

Imagine a subclass of MyItemClass (say MySubItemClass):

public class MySubItemClass : MyItemClass {}

Consider then that the type of MySubItemClass.Parent is still
MyClass<MyItemClass>, but a possible instantiation of MyClass<T> is
MyClass<MySubItemClass>. In this case (i.e. if MyClass<> was
instantiated with the MySubItemClass), the AppendItem() method would be
trying to assign a MyClass<MySubItemClass> to a field of type
MyClass<MyItemClass>.

That isn't allowed, because covariance isn't allowed by C# generics.

If A is a subclass of B, that does not mean that G<A> is a subclass of
where G<> is a generic type. Thus said:
Has anyone seen this problem and found a resolution?

You could make your item class generic. Consider this:

---8<---
using System;
using System.Collections.Generic;

class Program
{
class Container<T>
where T : Item<T>, new()
{
public List<T> Items = new List<T>();

public void Append()
{
T item = new T();
item.Container = this;
Items.Add(item);
}
}

class Item<TBase>
where TBase : Item<TBase>, new()
{
public Container<TBase> Container;
}

class SubItem : Item<SubItem>
{
}

static void Main()
{
Container<SubItem> container = new Container<SubItem>();
container.Append();
container.Items.ForEach(Console.WriteLine);
}
}
--->8---

-- Barry
 
A

Adam Clauss

Barry,
So from that point:
class Container<T>
where T : Item<T>, new()

class Item<TBase>
where TBase : Item<TBase>, new()

You show:
Container<SubItem> container = new Container<SubItem>();

Is there no easy way from this point though to decalre a list of ANY kind of
item?
Container<Item> container = new Container<Item>();
will not work because Item now requires template arguments.
 
L

Larry Lard

Adam said:
Barry,
So from that point:
class Container<T>
where T : Item<T>, new()

class Item<TBase>
where TBase : Item<TBase>, new()

You show:
Container<SubItem> container = new Container<SubItem>();

Is there no easy way from this point though to decalre a list of ANY kind of
item?
Container<Item> container = new Container<Item>();
will not work because Item now requires template arguments.

If there is some behavior which all Item<>s have in common - and this
must of course not depend on the type used to construct the Item<> -
then put that behavior in an interface IItem to be implemented by
Item<>. Then you can have a Container<IItem>.
 
B

Barry Kelly

Adam Clauss said:
Barry,
So from that point:
class Container<T>
where T : Item<T>, new()

class Item<TBase>
where TBase : Item<TBase>, new()

You show:
Container<SubItem> container = new Container<SubItem>();

Is there no easy way from this point though to decalre a list of ANY kind of
item?

You want your item to have a 'Container' reference. If you change Item
to be an interface (preferably with an 'I' prefix), you can do it with
interfaces instead.
Container<Item> container = new Container<Item>();
will not work because Item now requires template arguments.

The whole point of Item is for you to descend from it.

-- Barry
 
K

Kris

Hi All,

I would like to thank everyone for their response on this issue.

I was hoping to be able to do this is a fairly direct fashion. It seems
like a fundamental limitation of .NET generics that they don't support
inheritance of the generic types (as Barry indicated, "If A is a subclass
of B, that does not mean that G<A> is a subclass of G<B>, where
G<> is a generic type".)

I really wanted to avoid changing class inheritance heirarchy to solve
a generics problem. My concern is that, while I may get a complete
understanding of this now, 6 months from now I will need to rethink all
of this again when debugging some problem. I feel these things like this
need to be implemented in a fairly straight-forward manner so as to minimize
difficulty with maintaining the code.

I wonder what the reasons are for such limitations.


Kris
 
A

Adam Clauss

Barry Kelly said:
You want your item to have a 'Container' reference. If you change Item
to be an interface (preferably with an 'I' prefix), you can do it with
interfaces instead.

Thanks to both Larry and Barry.
Could one of you (or both) see a thread above this one from Lars (dated
7/21/06, 3:31pm). My question here stemmed from our discussion there, and I
think I have gone past my knowledge at this point.
Thanks!
 
B

Barry Kelly

Kris said:
I was hoping to be able to do this is a fairly direct fashion. It seems
like a fundamental limitation of .NET generics that they don't support
inheritance of the generic types (as Barry indicated, "If A is a subclass
of B, that does not mean that G<A> is a subclass of G<B>, where
G<> is a generic type".)

..NET does support it. C# doesn't. It is limited, though. I wrote an
article on it on my blog (see my signature, look for covariance and
contravariance). Consider:

List<Mammal> mammals = new List<Dog>();
mammals.Add(new Cat());

Taking this example into account, can you see the limitations of
supporting covariance with generics?
I really wanted to avoid changing class inheritance heirarchy to solve
a generics problem.

You don't need to change your class inheritance hierarchy - you can use
an interface instead.
I wonder what the reasons are for such limitations.

Generic types can support covariance and contravariance in limited
scenarios, depending on what operations are available on the types. For
more details, see my blog.

C# probably excluded them (at least for version 2.0) to avoid confusing
people with the limitations etc.

-- Barry
 
K

Kris

Barry,

Your blog brings to light some interesting points. I can see how support
for covariance and contravariance invites misuse. Still, without this
type of support (I don't know if covariance and contravariance would both
be needed), there are many problems that seem to be very difficult, if not
impossible, to implement using generics, as inheritance is necessary in
many cases. I guess we get to see if Micosoft decides to support this for
C# in .NET 3.0.

Thanks for all of your help.


Kris
 

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