Generics & Inheritence

R

ryanbreakspear

Hi all,

I have the classes BaseObject, and BaseList<T> to use as base classes
for some business objects.

I descend from both of these: DescendantObject: BaseObject and
DescendantList: BaseList<DescendantObject>.

How can I cast from a BaseList<BaseObject> to a DescendantList? Or
even a BaseList<BaseObject> to a BaseList<DescendantObject>?

I have put in a derivation constraint on the BaseList<T> so the
declaration looks like:

public class BaseList<T>: BindingList<T> where T: BaseObject

I would have thought that is enough to tell the compiler that it is
safe to perform the casts above.

Thanks in advance!
 
P

Peter Duniho

Hi all,

I have the classes BaseObject, and BaseList<T> to use as base classes
for some business objects.

I descend from both of these: DescendantObject: BaseObject and
DescendantList: BaseList<DescendantObject>.

How can I cast from a BaseList<BaseObject> to a DescendantList? Or
even a BaseList<BaseObject> to a BaseList<DescendantObject>?

You can't, not literally. That's not actually a valid relationship via
inheritance alone, and even using variance (which is supported in a
limited fashion in C# 4.0, soon to be released) you can't cast from
I have put in a derivation constraint on the BaseList<T> so the
declaration looks like:

public class BaseList<T>: BindingList<T> where T: BaseObject

I would have thought that is enough to tell the compiler that it is
safe to perform the casts above.

Knowing that T is BaseObject would not be enough to tell the compiler that
it is safe to cast T to DescendantObject, not in any context.

Depending on what you're really trying to do, it's possible that the
Enumerable.Cast<TResult>() method would accomplish what you want. It
won't let you get to a DescendantList or a BaseList<DescendantObject>
(which would be impossible), but as long as your list elements are valid
for casting, you can get a new IEnumerable<DescendantObject> out of it.

Note also that there's a pretty good chance that inheriting
List<DescendantObject> is the wrong approach anyway. It's not possible to
know for sure without seeing the full design and code, but inheriting a
generic collection like that just isn't usually appropriate.

Pete
 
B

Ben Voigt [C++ MVP]

Peter Duniho said:
You can't, not literally. That's not actually a valid relationship via
inheritance alone, and even using variance (which is supported in a
limited fashion in C# 4.0, soon to be released) you can't cast from
BaseList<BaseObject> to BaseList<DescendantObject>. You would only be
able to go the other way.

Since List supports both insertion and query, it is invariant, and you can't
go either way.
 
P

Peter Duniho

[...]
You can't, not literally. That's not actually a valid relationship via
inheritance alone, and even using variance (which is supported in a
limited fashion in C# 4.0, soon to be released) you can't cast from
BaseList<BaseObject> to BaseList<DescendantObject>. You would only be
able to go the other way.

Since List supports both insertion and query, it is invariant, and you
can't go either way.

Quite right. I conflated "list" and "enumerable" in my head. The basic
issue remains: what the OP is asking to do simply doesn't make sense, nor
would a related operation be supported by the language anwyay.
 
R

ryanbreakspear

[...]
How can I cast from a BaseList<BaseObject> to a DescendantList?  Or
even a BaseList<BaseObject> to a BaseList<DescendantObject>?
You can't, not literally.  That's not actually a valid relationship via  
inheritance alone, and even using variance (which is supported in a  
limited fashion in C# 4.0, soon to be released) you can't cast from  
BaseList<BaseObject> to BaseList<DescendantObject>.  You would only be  
able to go the other way.
Since List supports both insertion and query, it is invariant, and you  
can't go either way.

Quite right.  I conflated "list" and "enumerable" in my head.  The basic  
issue remains: what the OP is asking to do simply doesn't make sense, nor 
would a related operation be supported by the language anwyay.

Yes, sorry, was trying to cast from Descendant object list to Base
object list, my mistake in the original posting. I have a base
business object which has a pointer to it's owning collection (to send
list change events and various other things). In the descendant
business object I can't set the pointer because it is List<Descentant>
not List<Base>.

As I'm not using .Net 4.0, I guess the answer is that I can't do it.

I've also discovered some other annoying instances when I want to be
able to cast from a List<Descendant> to List<Base> so I will go back
to the original (Non generic) collection I was using based on
CollectionBase.

Thanks

Ryan
 
P

Peter Duniho

[...]
Quite right.  I conflated "list" and "enumerable" in my head.  The
basic  
issue remains: what the OP is asking to do simply doesn't make sense,
nor  
would a related operation be supported by the language anwyay.

Yes, sorry, was trying to cast from Descendant object list to Base
object list, my mistake in the original posting. I have a base
business object which has a pointer to it's owning collection (to send
list change events and various other things). In the descendant
business object I can't set the pointer because it is List<Descentant>
not List<Base>.

As I'm not using .Net 4.0, I guess the answer is that I can't do it.

You won't be able to do it in C# 4.0 either. As Ben pointed out, because
List<T> is mutable, it can't be variant in either direction, even the
theoretically safe one. If you could cast a List<Descendant> to a
List<Base>, then you could add _any_ Base-compatible element to your
even if that Base-derived class isn't Descendant said:
I've also discovered some other annoying instances when I want to be
able to cast from a List<Descendant> to List<Base> so I will go back
to the original (Non generic) collection I was using based on
CollectionBase.

Note that by doing this you will suffer exactly the problem that the
compiler error using generics is trying to help you avoid. It seems to me
that if you want to be able to treat the collection as a List<Base>, then
you should just use List<Base> as the type, rahter than List<Descendant>,
rather than going backwards to the non-type-safe, non-generic collection
types.

Alternatively, if you can claim that the List<Base> is used only in a safe
way, then there's a good chance that you don't really need a List<Base>,
but instead an IEnumerable<Base> would suffice. C$ 4.0 variance feature
will allow a straight assignment, but in the meantime the
IEnumerable.Cast<TResult>() method that I mentioned earlier will do
basically the same thing for you without variance support.

Pete
 
B

Ben Voigt [C++ MVP]

Peter Duniho said:
[...]
Since List supports both insertion and query, it is invariant, and
you
can't go either way.

Quite right. I conflated "list" and "enumerable" in my head. The
basic issue remains: what the OP is asking to do simply doesn't make
sense, nor would a related operation be supported by the language
anwyay.

Yes, sorry, was trying to cast from Descendant object list to Base
object list, my mistake in the original posting. I have a base
business object which has a pointer to it's owning collection (to send
list change events and various other things). In the descendant
business object I can't set the pointer because it is List<Descentant>
not List<Base>.

As I'm not using .Net 4.0, I guess the answer is that I can't do it.

You won't be able to do it in C# 4.0 either. As Ben pointed out, because
List<T> is mutable, it can't be variant in either direction, even the
theoretically safe one. If you could cast a List<Descendant> to a

It's not theoretically safe. The invariance isn't an implementation
constraint, it's theoretically correct, exactly because both data producing
and consuming operations are possible.
 
P

Peter Duniho

It's not theoretically safe. The invariance isn't an implementation
constraint, it's theoretically correct, exactly because both data
producing and consuming operations are possible.

Excuse me for being imprecise. "Theoretically safe" if we were talking
about a non-mutable collection. There is also the "theoretically safe" in
the other direction, for a collection to which things can only be added
but never retrieved, but I figured it's safe to ignore that possibility
for the sake of this discussion. :)

For List<T> specifically, you're right...but the hypothetical,
"theoretical" scenario I was writing about was something like
IEnumerable<T> which is read-only.

Pete
 

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