By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
432,569 Members | 1,386 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 432,569 IT Pros & Developers. It's quick & easy.

Casting generic collections & inheritance

P: n/a
OK, I have class A defined as follows:

class A
{
A(Queue<B> queue) { ... }
}

Now, I then have a subclass of both classes A and B. The subclass of A
(SubA), more specifically is passed a Queue<SubB>.

class SubA
{
SubA(Queue<SubB> queue)
: base (queue)
{ ... }
}

This, at first glance appears to be correct, because if a queue contains all
elements of type SubB, then those by definition are also of type B. So, the
types should be compatible.

However, upon compiling, I get an error saying it cannot convert the types.
Further, Queue does not have a Convert() method, as the List does. Any
suggestions on how to solve this?

Thanks
--
Adam Clauss

Jan 11 '06 #1
Share this Question
Share on Google+
22 Replies


P: n/a
"Adam Clauss" <ca*****@tamu.edu> a écrit dans le message de news:
11*************@corp.supernews.com...

| This, at first glance appears to be correct, because if a queue contains
all
| elements of type SubB, then those by definition are also of type B. So,
the
| types should be compatible.
|
| However, upon compiling, I get an error saying it cannot convert the
types.
| Further, Queue does not have a Convert() method, as the List does. Any
| suggestions on how to solve this?

If you inherited from Queue<T>, then you would be able to assign sub-classes
of Queue<T>, but Queue<SubB> does not inherit from Queue<B>, it is regarded
as a separate class.

inheritance of generic classes relies on the generic type, not the parameter
type.

Joanna

--
Joanna Carter [TeamB]
Consultant Software Engineer
Jan 11 '06 #2

P: n/a
Hmm... you would think the compiler would be able to make that distinction -
that a Queue<SubB> is also a Queue<B>.

--
Adam Clauss

"Joanna Carter [TeamB]" <jo****@not.for.spam> wrote in message
news:eq**************@TK2MSFTNGP10.phx.gbl...
"Adam Clauss" <ca*****@tamu.edu> a écrit dans le message de news:
11*************@corp.supernews.com...

| This, at first glance appears to be correct, because if a queue contains
all
| elements of type SubB, then those by definition are also of type B. So,
the
| types should be compatible.
|
| However, upon compiling, I get an error saying it cannot convert the
types.
| Further, Queue does not have a Convert() method, as the List does. Any
| suggestions on how to solve this?

If you inherited from Queue<T>, then you would be able to assign
sub-classes
of Queue<T>, but Queue<SubB> does not inherit from Queue<B>, it is
regarded
as a separate class.

inheritance of generic classes relies on the generic type, not the
parameter
type.

Joanna

--
Joanna Carter [TeamB]
Consultant Software Engineer

Jan 11 '06 #3

P: n/a
"Adam Clauss" <ca*****@tamu.edu> wrote in
news:11*************@corp.supernews.com:
Hmm... you would think the compiler would be able to make that
distinction - that a Queue<SubB> is also a Queue<B>.


I had been wondering about that too... Although it can't cast directly,
you can add each item to a new list..

Can any of the smarter people here explain why this type of generic
inheritance wasn't included in the language/compiler?

Here's some sample code... Note in particular the two comments about
halfway down. (Sorry for the bad wrapping - is there a better way to
do this? Should I just attach the file in the future??)

using System;
using System.Collections.Generic;
using System.Text;

namespace GenericTypeInheritanceTest
{
class Program
{
static void Main(string[] args)
{
List<Person> people = new List<Person>();

Person mike = new Person("mike");
Farmer john = new Farmer("john", 10);
Farmer bob = new Farmer("bob", 20);

people.Add(mike);
people.Add(john);

Console.WriteLine("People:");
foreach (Person p in people) Console.WriteLine
(p.ToString());

List<Farmer> farmers = new List<Farmer>();
farmers.Add(john);
farmers.Add(bob);
Console.WriteLine();
Console.WriteLine("Farmers:");
foreach (Farmer f in farmers) Console.WriteLine
(f.ToString());

// Doesn't compile
// List<Person> otherPeople = new List<Person>
(farmers);
List<Person> otherPeople = new List<Person>();

// But this works just like you would expect!

foreach (Farmer f in farmers) otherPeople.Add(f);
Console.WriteLine();
Console.WriteLine("Other People:");
foreach (Person p in otherPeople) Console.WriteLine
(p.ToString());

Console.ReadLine();
}
}

public class Person
{
public Person(string name)
{
this.name = name;
}

public string name;

public override string ToString()
{
return "Name=" + name;
}
}

public class Farmer : Person
{
public Farmer(string name, int acresOfFarmLand)
: base(name)
{
this.acresOfFarmLand = acresOfFarmLand;
}

int acresOfFarmLand;

public override string ToString()
{
return base.ToString() + "; Acres=" +
acresOfFarmLand.ToString();
}
}
}
Jan 12 '06 #4

P: n/a
Michael Bray <mbray@makeDIntoDot_ctiusaDcom> wrote in
news:Xn****************************@207.46.248.16:
Can any of the smarter people here explain why this type of generic
inheritance wasn't included in the language/compiler?


Let me say before this gets too far... I understand WHY it doesn't
compile... So please no discussion about how a List<Person> isn't the
same as a List<Farmer> or vice-versa... I want to know why support for
this isn't included in the language/compiler... :)

-mdb
Jan 12 '06 #5

P: n/a
Adam Clauss <ca*****@tamu.edu> wrote:
Hmm... you would think the compiler would be able to make that distinction -
that a Queue<SubB> is also a Queue<B>.


No it's not. If you could treat a Queue<string> as a Queue<object> then
you could add a new object() to it - and suddenly anything viewing it
as a Queue<string> would be in trouble.

I guess the reverse question is why covariance *is* allowed for
arrays...

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Jan 12 '06 #6

P: n/a
Michael Bray <mbray@makeDIntoDot_ctiusaDcom> wrote:
"Adam Clauss" <ca*****@tamu.edu> wrote in
news:11*************@corp.supernews.com:
Hmm... you would think the compiler would be able to make that
distinction - that a Queue<SubB> is also a Queue<B>.


I had been wondering about that too... Although it can't cast directly,
you can add each item to a new list..

Can any of the smarter people here explain why this type of generic
inheritance wasn't included in the language/compiler?


Well, the C# spec puts it like this:

<quote>
No special conversions exist between constructed reference types other
than those described in §15. In particular, unlike array types,
constructed reference types do not exhibit =3Fco-variant=3F conversions..

This means that a type List<B> has no conversion (either implicit or
explicit) to List<A> even if B is derived from A. Likewise, no
conversion exists from List<B> to List<object>.

[Note: The rationale for this is simple: if a conversion to List<A> is
permitted, then apparently, one can store values of type A into the
list. However, this would break the invariant that every object in a
list of type List<B> is always a value of type B, or else unexpected
failures can occur when assigning into collection classes. end note]
</quote>

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Jan 12 '06 #7

P: n/a
Jon Skeet [C# MVP] <sk***@pobox.com> wrote in
news:MP************************@msnews.microsoft.c om:
No it's not. If you could treat a Queue<string> as a Queue<object> then you could add a new object() to it - and suddenly anything viewing it
as a Queue<string> would be in trouble.


Jon, this is backwards to what Adam is saying... Its not that we want
to insert objects into the List<string> but we (well, me at least) would
like to be able to, for example, pass a List<string> to a function that
takes a List<object>.

So for example, something like this is not possible, but it sure would
be nice:

public class Person { ... }
public class Farmer : Person { ... }
public void ShowNames(List<Person> people)
{
foreach(Person p in people) Console.WriteLine(p.Name);
}
public void ShowFarmerNames(List<Farmer> farmers)
{
// Can't do this:
// ShowNames(farmers);

// Have to do this:
foreach(Farmer f in farmers) Console.WriteLine(f.Name);
}
Although after writing this example, I can see where the problem is...
the ShowNames function in this case doesn't modify the list, but that's
only because of the nature of the function. Some other function that
took a List<Person> might want to modify it and add a Person object
which would violate the definition of the List<Farmer>. So I guess it
makes sense after all! :)

For anyone else following this thread, though, here's two nice ways to
convert (for Lists, at least from one type to another). Although I will
add that in light of the above, this should only be used for temporary
conversion in the case that you want to pass a List<DerivedClass> to a
function that processes a List<BaseClass> in a read-only fashion.

otherPeople = new List<Person>(farmers.ToArray());

otherPeople = farmers.ConvertAll(new Converter<Farmer, Person>(
delegate(Farmer f) { return (Person)f; }
));

The second one actually is more generic and doesn't even require that
the two classes be a base/derived class pair. In the example given,
however, this is the case, which is why the anonymous method code is
just a simple cast from Farmer to Person.

-mdb
Jan 12 '06 #8

P: n/a
Michael Bray wrote:
No it's not. If you could treat a Queue<string> as a Queue<object>
then you could add a new object() to it - and suddenly anything viewing it
as a Queue<string> would be in trouble.
Jon, this is backwards to what Adam is saying... Its not that we want
to insert objects into the List<string> but we (well, me at least) would
like to be able to, for example, pass a List<string> to a function that
takes a List<object>.


That's *exactly* what I said - if you could pass a List<string> to
something expecting List<object> that's treating a List<string> as a
List<object>, isn't it?

The method you would pass the list to could do:

theList.Add (new object());

What would you expect to happen at that point? The caller is still
expecting every element to be a string, so either you allow it and they
blow up when they use the element as a string, or you blow up at the
call to Add (which is what array covariance effectively does).
So for example, something like this is not possible, but it sure would
be nice:
<snip>
Although after writing this example, I can see where the problem is...
the ShowNames function in this case doesn't modify the list, but that's
only because of the nature of the function. Some other function that
took a List<Person> might want to modify it and add a Person object
which would violate the definition of the List<Farmer>. So I guess it
makes sense after all! :)
Exactly. That's what I was trying to say. Just out of interest, how
could I have expressed it more clearly? It's a fairly common question -
I'd like to know how to address it better. Would including source code
have helped.
For anyone else following this thread, though, here's two nice ways to
convert (for Lists, at least from one type to another). Although I will
add that in light of the above, this should only be used for temporary
conversion in the case that you want to pass a List<DerivedClass> to a
function that processes a List<BaseClass> in a read-only fashion.

otherPeople = new List<Person>(farmers.ToArray());

otherPeople = farmers.ConvertAll(new Converter<Farmer, Person>(
delegate(Farmer f) { return (Person)f; }
));
I haven't tried, but I'm sure there's an easy way of writing a generic
converter that can always do an identity conversion (i.e. convert from
S to T where S : T).
The second one actually is more generic and doesn't even require that
the two classes be a base/derived class pair. In the example given,
however, this is the case, which is why the anonymous method code is
just a simple cast from Farmer to Person.


In fact, the cast isn't even needed, is it?

Jon

Jan 12 '06 #9

P: n/a
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:11*********************@g44g2000cwa.googlegro ups.com...
Michael Bray wrote:
> No it's not. If you could treat a Queue<string> as a Queue<object>
> then you could add a new object() to it - and suddenly anything
> viewing it
> as a Queue<string> would be in trouble.


Jon, this is backwards to what Adam is saying... Its not that we want
to insert objects into the List<string> but we (well, me at least) would
like to be able to, for example, pass a List<string> to a function that
takes a List<object>.


That's *exactly* what I said - if you could pass a List<string> to
something expecting List<object> that's treating a List<string> as a
List<object>, isn't it?


Thanks to everyone for their comments. It now makes sense to me WHY it does
not work. Additionally, I have come up with what I think is a rather good
solution to it.

First, the problem with copying the list and adding the elements to a NEW
queue, is that I needed both parent and subclass to be referencing the same
queue - not a COPY of a queue.

What allowed this to work is - make the base A<> class generic.

class A<T> : where T: B
{
A(Queue<T> queue) { ... }
}

class SubA : A<SubB>
{
SubA(Queue<SubB> queue)
: base (queue)
{ ... }
}

This allows A to make it's casts to B when necessary, but still disallows
adding invalid objects to the queue. Jon, any flaws you see in what I've
done?

--
Adam Clauss
Jan 12 '06 #10

P: n/a
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in
news:11*********************@g44g2000cwa.googlegro ups.com:
Michael Bray wrote:
> No it's not. If you could treat a Queue<string> as a Queue<object>
> then you could add a new object() to it - and suddenly anything
> viewing it as a Queue<string> would be in trouble.
Jon, this is backwards to what Adam is saying... Its not that we
want to insert objects into the List<string> but we (well, me at
least) would like to be able to, for example, pass a List<string> to
a function that takes a List<object>.


That's *exactly* what I said - if you could pass a List<string> to
something expecting List<object> that's treating a List<string> as a
List<object>, isn't it?


Yeah I guess that its a matter of semantical grouping in English... in
other words, and keeping with my 'Farmer is a Person' definition,

A "List of (Farmers)" is most definitely a "List of (People)",

BUT

A "(List of Farmers)" is NOT a "(List of People)".

The phrasing just gets in the way. Who's up for speaking in binary?
Exactly. That's what I was trying to say. Just out of interest, how
could I have expressed it more clearly? It's a fairly common question
- I'd like to know how to address it better. Would including source
code have helped.


I think you phrased it as best you can from a technical perspective. I
think the problem is that people who are new to generics (such as
myself) don't perceive a List<object> as a "(thing)" but as a "list of
(things)" which goes back to the English grouping problem.
The second one actually is more generic and doesn't even require that
the two classes be a base/derived class pair. In the example given,
however, this is the case, which is why the anonymous method code is
just a simple cast from Farmer to Person.


In fact, the cast isn't even needed, is it?


True... I guess I just put it there (unintentionally) to be clear that
I was returning a Person object (self-documenting code is still in
fashion, no?).

FWIW, I guess I could say at this point that it would still be nice to
be able to call ShowNames(List<People>) but there would have to be a
restriction that ShowNames (and any other functions it calls) can't
modify the list, eg something like ShowNames(readonly List<People>).
Wishful thinking I guess.

-mdb
Jan 12 '06 #11

P: n/a
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in
news:11*********************@g44g2000cwa.googlegro ups.com:
Would including source code
have helped.


Definitely. In fact, writing the source code is what let me understand
what the real problem was... Just make sure that the function is one that
modifies the list to show where the problem is.

-mdb
Jan 12 '06 #12

P: n/a

Adam Clauss wrote:
Thanks to everyone for their comments. It now makes sense to me WHY it does
not work. Additionally, I have come up with what I think is a rather good
solution to it.

First, the problem with copying the list and adding the elements to a NEW
queue, is that I needed both parent and subclass to be referencing the same
queue - not a COPY of a queue.

What allowed this to work is - make the base A<> class generic.
<snip>
This allows A to make it's casts to B when necessary, but still disallows
adding invalid objects to the queue. Jon, any flaws you see in what I've
done?


Not from the above - but do you definitely need a new class (SubA)? Is
it adding different capabilities to A? Don't forget you can just do:

A<SubB> x = new A(new Queue<SubB>());

in your client code.

I'm not saying you don't need the extra class - just checking that
you've thought of it.

Jon

Jan 12 '06 #13

P: n/a
Michael Bray wrote:
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in
news:11*********************@g44g2000cwa.googlegro ups.com:
Would including source code have helped.


Definitely. In fact, writing the source code is what let me understand
what the real problem was... Just make sure that the function is one that
modifies the list to show where the problem is.


Righto. I'll try to get it into the FAQ fairly soon... (at least my web
page, and possibly the MSDN one too.)

Jon

Jan 12 '06 #14

P: n/a
Michael Bray wrote:
FWIW, I guess I could say at this point that it would still be nice to
be able to call ShowNames(List<People>) but there would have to be a
restriction that ShowNames (and any other functions it calls) can't
modify the list, eg something like ShowNames(readonly List<People>).
Wishful thinking I guess.


What you *could* do is

void ShowNames<T>(List <T>) where T : Person

(I believe, anyway.)

Jon

Jan 12 '06 #15

P: n/a
Yes - SubA adds additional functionality. There are several overrides (and
completely new methods). I didn't include them here, didn't feel they were
completely relevant to the issue w/ generic and inheritance.

Thanks

--
Adam Clauss

"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:11**********************@z14g2000cwz.googlegr oups.com...

Adam Clauss wrote:
Thanks to everyone for their comments. It now makes sense to me WHY it
does
not work. Additionally, I have come up with what I think is a rather
good
solution to it.

First, the problem with copying the list and adding the elements to a NEW
queue, is that I needed both parent and subclass to be referencing the
same
queue - not a COPY of a queue.

What allowed this to work is - make the base A<> class generic.


<snip>
This allows A to make it's casts to B when necessary, but still disallows
adding invalid objects to the queue. Jon, any flaws you see in what I've
done?


Not from the above - but do you definitely need a new class (SubA)? Is
it adding different capabilities to A? Don't forget you can just do:

A<SubB> x = new A(new Queue<SubB>());

in your client code.

I'm not saying you don't need the extra class - just checking that
you've thought of it.

Jon

Jan 12 '06 #16

P: n/a
Michael Bray <mbray@makeDIntoDot_ctiusaDcom> wrote:
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in
news:11*********************@g47g2000cwa.googlegro ups.com:
What you *could* do is

void ShowNames<T>(List <T>) where T : Person

(I believe, anyway.)
You are absolutely right! That works great!


Goodo :)
BTW I have attached the test source code I used... is this a better way
to provide code samples to the newsgroup? I assume that in the least it
will handle the wrapping problem... Let me know.


It's generally simpler if you just include the code inline, having done
any appropriate wrapping to make the lines less than 80 characters.
It's much easier to see what's going on that way, IMO.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Jan 12 '06 #17

P: n/a
Random aside: the logic I can see here makes perfect sence for anything that
allows addition etc - however, some casts could be sensible defined. In
particular (and most usefully) couldn't a List<X> quite safely implement
IEnumerable<T> for each T in X's inheritance tree?

Unless I am missing something this fails at the moment, but would be very
useful (to me, at least).

Example (which doesn't work) below.

Any thoughts? Am I missing an obvious "this wouldn't work because..."?

public class A {
public readonly string Name; // [sic] for brevity / demo
public A(string name) { name = Name; }
}
public class B : A {
public readonly int ID;
public B(string name, int id) : base(name) { id = ID; }
}
public class Program {

public static void Main() {
List<B> list = new List<B>();
list.Add(new B("Fred", 1));
list.Add(new B("Barney", 2));
list.Add(new B("Wilma", 3));
list.Add(new B("Betty", 4));
// *** following fails (as per spec) at runtime
// (would be nice if a: worked, and b: didn't need cast)
IEnumerable<A> items = (IEnumerable<A>) list;
foreach(A item in items) {
Console.WriteLine(item.Name);
}
Console.ReadLine();

}
}

"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:11*********************@g44g2000cwa.googlegro ups.com...
Michael Bray wrote:
> No it's not. If you could treat a Queue<string> as a Queue<object>
> then you could add a new object() to it - and suddenly anything
> viewing it
> as a Queue<string> would be in trouble.


Jon, this is backwards to what Adam is saying... Its not that we want
to insert objects into the List<string> but we (well, me at least) would
like to be able to, for example, pass a List<string> to a function that
takes a List<object>.


That's *exactly* what I said - if you could pass a List<string> to
something expecting List<object> that's treating a List<string> as a
List<object>, isn't it?

The method you would pass the list to could do:

theList.Add (new object());

What would you expect to happen at that point? The caller is still
expecting every element to be a string, so either you allow it and they
blow up when they use the element as a string, or you blow up at the
call to Add (which is what array covariance effectively does).
So for example, something like this is not possible, but it sure would
be nice:


<snip>
Although after writing this example, I can see where the problem is...
the ShowNames function in this case doesn't modify the list, but that's
only because of the nature of the function. Some other function that
took a List<Person> might want to modify it and add a Person object
which would violate the definition of the List<Farmer>. So I guess it
makes sense after all! :)


Exactly. That's what I was trying to say. Just out of interest, how
could I have expressed it more clearly? It's a fairly common question -
I'd like to know how to address it better. Would including source code
have helped.
For anyone else following this thread, though, here's two nice ways to
convert (for Lists, at least from one type to another). Although I will
add that in light of the above, this should only be used for temporary
conversion in the case that you want to pass a List<DerivedClass> to a
function that processes a List<BaseClass> in a read-only fashion.

otherPeople = new List<Person>(farmers.ToArray());

otherPeople = farmers.ConvertAll(new Converter<Farmer, Person>(
delegate(Farmer f) { return (Person)f; }
));


I haven't tried, but I'm sure there's an easy way of writing a generic
converter that can always do an identity conversion (i.e. convert from
S to T where S : T).
The second one actually is more generic and doesn't even require that
the two classes be a base/derived class pair. In the example given,
however, this is the case, which is why the anonymous method code is
just a simple cast from Farmer to Person.


In fact, the cast isn't even needed, is it?

Jon

Jan 13 '06 #18

P: n/a
minor typo - doesn't affect my point... ctor code should be reversed
(currently sets variable to the field)...

also - obviously it would have to use explicit implementation (rather than
public implementation) for the different Ts, but IIRC this isn't a problem
and works currently.

Marc

"Marc Gravell" <mg******@rm.com> wrote in message
news:OA****************@tk2msftngp13.phx.gbl...
Random aside: the logic I can see here makes perfect sence for anything
that allows addition etc - however, some casts could be sensible defined.
In particular (and most usefully) couldn't a List<X> quite safely
implement IEnumerable<T> for each T in X's inheritance tree?

Unless I am missing something this fails at the moment, but would be very
useful (to me, at least).

Example (which doesn't work) below.

Any thoughts? Am I missing an obvious "this wouldn't work because..."?

public class A {
public readonly string Name; // [sic] for brevity / demo
public A(string name) { name = Name; }
}
public class B : A {
public readonly int ID;
public B(string name, int id) : base(name) { id = ID; }
}
public class Program {

public static void Main() {
List<B> list = new List<B>();
list.Add(new B("Fred", 1));
list.Add(new B("Barney", 2));
list.Add(new B("Wilma", 3));
list.Add(new B("Betty", 4));
// *** following fails (as per spec) at runtime
// (would be nice if a: worked, and b: didn't need cast)
IEnumerable<A> items = (IEnumerable<A>) list;
foreach(A item in items) {
Console.WriteLine(item.Name);
}
Console.ReadLine();

}
}

"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:11*********************@g44g2000cwa.googlegro ups.com...
Michael Bray wrote:
> No it's not. If you could treat a Queue<string> as a Queue<object>
> then you could add a new object() to it - and suddenly anything
> viewing it
> as a Queue<string> would be in trouble.

Jon, this is backwards to what Adam is saying... Its not that we want
to insert objects into the List<string> but we (well, me at least) would
like to be able to, for example, pass a List<string> to a function that
takes a List<object>.


That's *exactly* what I said - if you could pass a List<string> to
something expecting List<object> that's treating a List<string> as a
List<object>, isn't it?

The method you would pass the list to could do:

theList.Add (new object());

What would you expect to happen at that point? The caller is still
expecting every element to be a string, so either you allow it and they
blow up when they use the element as a string, or you blow up at the
call to Add (which is what array covariance effectively does).
So for example, something like this is not possible, but it sure would
be nice:


<snip>
Although after writing this example, I can see where the problem is...
the ShowNames function in this case doesn't modify the list, but that's
only because of the nature of the function. Some other function that
took a List<Person> might want to modify it and add a Person object
which would violate the definition of the List<Farmer>. So I guess it
makes sense after all! :)


Exactly. That's what I was trying to say. Just out of interest, how
could I have expressed it more clearly? It's a fairly common question -
I'd like to know how to address it better. Would including source code
have helped.
For anyone else following this thread, though, here's two nice ways to
convert (for Lists, at least from one type to another). Although I will
add that in light of the above, this should only be used for temporary
conversion in the case that you want to pass a List<DerivedClass> to a
function that processes a List<BaseClass> in a read-only fashion.

otherPeople = new List<Person>(farmers.ToArray());

otherPeople = farmers.ConvertAll(new Converter<Farmer, Person>(
delegate(Farmer f) { return (Person)f; }
));


I haven't tried, but I'm sure there's an easy way of writing a generic
converter that can always do an identity conversion (i.e. convert from
S to T where S : T).
The second one actually is more generic and doesn't even require that
the two classes be a base/derived class pair. In the example given,
however, this is the case, which is why the anonymous method code is
just a simple cast from Farmer to Person.


In fact, the cast isn't even needed, is it?

Jon


Jan 13 '06 #19

P: n/a
For reference, I found a partial solution to this involving generics (i.e.
making Func<T>(IEnumerable<T>) where T : A); in the following, the main crux
is that A shouldn't need to have any knowledge of B; this then allows me to
pass a Collection<B> or List<B> to a function that expects IEnumerable<A>

Marc

public class A {
public readonly string Name;
public A(string name) { Name = name; }

public static void TestList<T>(IEnumerable<T> items)
where T : A {

foreach (A item in items) {
Console.WriteLine(item.Name);
}
}
}
public class B : A {
public readonly int ID;
public B(string name, int id) : base(name) { ID = id; }
}
public static class Program {

public static void Main() {
List<B> list = new List<B>();
list.Add(new B("Fred", 1));
list.Add(new B("Barney", 2));
list.Add(new B("Wilma", 3));
list.Add(new B("Betty", 4));
A.TestList<B>(list);
Console.ReadLine();
}
}

"Marc Gravell" <mg******@rm.com> wrote in message
news:OA****************@tk2msftngp13.phx.gbl...
Random aside: the logic I can see here makes perfect sence for anything
that allows addition etc - however, some casts could be sensible defined.
In particular (and most usefully) couldn't a List<X> quite safely
implement IEnumerable<T> for each T in X's inheritance tree?

Unless I am missing something this fails at the moment, but would be very
useful (to me, at least).

Example (which doesn't work) below.

Any thoughts? Am I missing an obvious "this wouldn't work because..."?

public class A {
public readonly string Name; // [sic] for brevity / demo
public A(string name) { name = Name; }
}
public class B : A {
public readonly int ID;
public B(string name, int id) : base(name) { id = ID; }
}
public class Program {

public static void Main() {
List<B> list = new List<B>();
list.Add(new B("Fred", 1));
list.Add(new B("Barney", 2));
list.Add(new B("Wilma", 3));
list.Add(new B("Betty", 4));
// *** following fails (as per spec) at runtime
// (would be nice if a: worked, and b: didn't need cast)
IEnumerable<A> items = (IEnumerable<A>) list;
foreach(A item in items) {
Console.WriteLine(item.Name);
}
Console.ReadLine();

}
}

"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:11*********************@g44g2000cwa.googlegro ups.com...
Michael Bray wrote:
> No it's not. If you could treat a Queue<string> as a Queue<object>
> then you could add a new object() to it - and suddenly anything
> viewing it
> as a Queue<string> would be in trouble.

Jon, this is backwards to what Adam is saying... Its not that we want
to insert objects into the List<string> but we (well, me at least) would
like to be able to, for example, pass a List<string> to a function that
takes a List<object>.


That's *exactly* what I said - if you could pass a List<string> to
something expecting List<object> that's treating a List<string> as a
List<object>, isn't it?

The method you would pass the list to could do:

theList.Add (new object());

What would you expect to happen at that point? The caller is still
expecting every element to be a string, so either you allow it and they
blow up when they use the element as a string, or you blow up at the
call to Add (which is what array covariance effectively does).
So for example, something like this is not possible, but it sure would
be nice:


<snip>
Although after writing this example, I can see where the problem is...
the ShowNames function in this case doesn't modify the list, but that's
only because of the nature of the function. Some other function that
took a List<Person> might want to modify it and add a Person object
which would violate the definition of the List<Farmer>. So I guess it
makes sense after all! :)


Exactly. That's what I was trying to say. Just out of interest, how
could I have expressed it more clearly? It's a fairly common question -
I'd like to know how to address it better. Would including source code
have helped.
For anyone else following this thread, though, here's two nice ways to
convert (for Lists, at least from one type to another). Although I will
add that in light of the above, this should only be used for temporary
conversion in the case that you want to pass a List<DerivedClass> to a
function that processes a List<BaseClass> in a read-only fashion.

otherPeople = new List<Person>(farmers.ToArray());

otherPeople = farmers.ConvertAll(new Converter<Farmer, Person>(
delegate(Farmer f) { return (Person)f; }
));


I haven't tried, but I'm sure there's an easy way of writing a generic
converter that can always do an identity conversion (i.e. convert from
S to T where S : T).
The second one actually is more generic and doesn't even require that
the two classes be a base/derived class pair. In the example given,
however, this is the case, which is why the anonymous method code is
just a simple cast from Farmer to Person.


In fact, the cast isn't even needed, is it?

Jon


Jan 13 '06 #20

P: n/a
(just realised - this is in fact the same as Jon's solution... d'oh!)

A minor aside (that I didn't expect to work) is that even though the
function is defined as SomeFunc<A>(param), you don't always actually need to
specify A - it derives it from the parameter... very neat.

Marc

"Marc Gravell" <mg******@rm.com> wrote in message
news:ek**************@TK2MSFTNGP11.phx.gbl...
For reference, I found a partial solution to this involving generics (i.e.
making Func<T>(IEnumerable<T>) where T : A); in the following, the main
crux is that A shouldn't need to have any knowledge of B; this then allows
me to pass a Collection<B> or List<B> to a function that expects
IEnumerable<A>

Marc

public class A {
public readonly string Name;
public A(string name) { Name = name; }

public static void TestList<T>(IEnumerable<T> items)
where T : A {

foreach (A item in items) {
Console.WriteLine(item.Name);
}
}
}
public class B : A {
public readonly int ID;
public B(string name, int id) : base(name) { ID = id; }
}
public static class Program {

public static void Main() {
List<B> list = new List<B>();
list.Add(new B("Fred", 1));
list.Add(new B("Barney", 2));
list.Add(new B("Wilma", 3));
list.Add(new B("Betty", 4));
A.TestList<B>(list);
Console.ReadLine();
}
}

"Marc Gravell" <mg******@rm.com> wrote in message
news:OA****************@tk2msftngp13.phx.gbl...
Random aside: the logic I can see here makes perfect sence for anything
that allows addition etc - however, some casts could be sensible defined.
In particular (and most usefully) couldn't a List<X> quite safely
implement IEnumerable<T> for each T in X's inheritance tree?

Unless I am missing something this fails at the moment, but would be very
useful (to me, at least).

Example (which doesn't work) below.

Any thoughts? Am I missing an obvious "this wouldn't work because..."?

public class A {
public readonly string Name; // [sic] for brevity / demo
public A(string name) { name = Name; }
}
public class B : A {
public readonly int ID;
public B(string name, int id) : base(name) { id = ID; }
}
public class Program {

public static void Main() {
List<B> list = new List<B>();
list.Add(new B("Fred", 1));
list.Add(new B("Barney", 2));
list.Add(new B("Wilma", 3));
list.Add(new B("Betty", 4));
// *** following fails (as per spec) at runtime
// (would be nice if a: worked, and b: didn't need cast)
IEnumerable<A> items = (IEnumerable<A>) list;
foreach(A item in items) {
Console.WriteLine(item.Name);
}
Console.ReadLine();

}
}

"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:11*********************@g44g2000cwa.googlegro ups.com...
Michael Bray wrote:
> No it's not. If you could treat a Queue<string> as a Queue<object>
> then you could add a new object() to it - and suddenly anything
> viewing it
> as a Queue<string> would be in trouble.

Jon, this is backwards to what Adam is saying... Its not that we want
to insert objects into the List<string> but we (well, me at least)
would
like to be able to, for example, pass a List<string> to a function that
takes a List<object>.

That's *exactly* what I said - if you could pass a List<string> to
something expecting List<object> that's treating a List<string> as a
List<object>, isn't it?

The method you would pass the list to could do:

theList.Add (new object());

What would you expect to happen at that point? The caller is still
expecting every element to be a string, so either you allow it and they
blow up when they use the element as a string, or you blow up at the
call to Add (which is what array covariance effectively does).

So for example, something like this is not possible, but it sure would
be nice:

<snip>

Although after writing this example, I can see where the problem is...
the ShowNames function in this case doesn't modify the list, but that's
only because of the nature of the function. Some other function that
took a List<Person> might want to modify it and add a Person object
which would violate the definition of the List<Farmer>. So I guess it
makes sense after all! :)

Exactly. That's what I was trying to say. Just out of interest, how
could I have expressed it more clearly? It's a fairly common question -
I'd like to know how to address it better. Would including source code
have helped.

For anyone else following this thread, though, here's two nice ways to
convert (for Lists, at least from one type to another). Although I
will
add that in light of the above, this should only be used for temporary
conversion in the case that you want to pass a List<DerivedClass> to a
function that processes a List<BaseClass> in a read-only fashion.

otherPeople = new List<Person>(farmers.ToArray());

otherPeople = farmers.ConvertAll(new Converter<Farmer, Person>(
delegate(Farmer f) { return (Person)f; }
));

I haven't tried, but I'm sure there's an easy way of writing a generic
converter that can always do an identity conversion (i.e. convert from
S to T where S : T).

The second one actually is more generic and doesn't even require that
the two classes be a base/derived class pair. In the example given,
however, this is the case, which is why the anonymous method code is
just a simple cast from Farmer to Person.

In fact, the cast isn't even needed, is it?

Jon



Jan 13 '06 #21

P: n/a
Marc Gravell <mg******@rm.com> wrote:
Random aside: the logic I can see here makes perfect sence for anything that
allows addition etc - however, some casts could be sensible defined. In
particular (and most usefully) couldn't a List<X> quite safely implement
IEnumerable<T> for each T in X's inheritance tree?


Yes, quite possibly. However, I suspect that codifying the rules for
this kind of thing would be pretty horrendous. It shouldn't be too hard
to write something which implements IEnumerable<T> given an
IEnumerable<X> where T : X, if you find yourself doing this often.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Jan 13 '06 #22

P: n/a
Yes; after finding this "{base}<X>" ==> "{base}<T> where T : X" solution
(which, to be fair, you posted before I mentioned it), I now 100% agree;
there is a simple, neat solution to this *specific* problem that also
addresses the more complex "Add" scenario, so I agree: stick to one route to
solve the same problems.

Marc

"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MP***********************@msnews.microsoft.co m...
Marc Gravell <mg******@rm.com> wrote:
Random aside: the logic I can see here makes perfect sence for anything
that
allows addition etc - however, some casts could be sensible defined. In
particular (and most usefully) couldn't a List<X> quite safely implement
IEnumerable<T> for each T in X's inheritance tree?


Yes, quite possibly. However, I suspect that codifying the rules for
this kind of thing would be pretty horrendous. It shouldn't be too hard
to write something which implements IEnumerable<T> given an
IEnumerable<X> where T : X, if you find yourself doing this often.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Jan 14 '06 #23

This discussion thread is closed

Replies have been disabled for this discussion.