473,836 Members | 2,185 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Casting generic collections & inheritance

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
22 13136
"Adam Clauss" <ca*****@tamu.e du> 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
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******** ******@TK2MSFTN GP10.phx.gbl...
"Adam Clauss" <ca*****@tamu.e du> 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
"Adam Clauss" <ca*****@tamu.e du> wrote in
news:11******** *****@corp.supe rnews.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.Collecti ons.Generic;
using System.Text;

namespace GenericTypeInhe ritanceTest
{
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.WriteLi ne("People:");
foreach (Person p in people) Console.WriteLi ne
(p.ToString());

List<Farmer> farmers = new List<Farmer>();
farmers.Add(joh n);
farmers.Add(bob );
Console.WriteLi ne();
Console.WriteLi ne("Farmers:") ;
foreach (Farmer f in farmers) Console.WriteLi ne
(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.WriteLi ne();
Console.WriteLi ne("Other People:");
foreach (Person p in otherPeople) Console.WriteLi ne
(p.ToString());

Console.ReadLin e();
}
}

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.acresOfFar mLand = acresOfFarmLand ;
}

int acresOfFarmLand ;

public override string ToString()
{
return base.ToString() + "; Acres=" +
acresOfFarmLand .ToString();
}
}
}
Jan 12 '06 #4
Michael Bray <mbray@makeDInt oDot_ctiusaDcom > wrote in
news:Xn******** *************** *****@207.46.24 8.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
Adam Clauss <ca*****@tamu.e du> 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.co m>
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
Michael Bray <mbray@makeDInt oDot_ctiusaDcom > wrote:
"Adam Clauss" <ca*****@tamu.e du> wrote in
news:11******** *****@corp.supe rnews.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.co m>
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
Jon Skeet [C# MVP] <sk***@pobox.co m> wrote in
news:MP******** *************** *@msnews.micros oft.com:
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.WriteLi ne(p.Name);
}
public void ShowFarmerNames (List<Farmer> farmers)
{
// Can't do this:
// ShowNames(farme rs);

// Have to do this:
foreach(Farmer f in farmers) Console.WriteLi ne(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<DerivedCla ss> to a
function that processes a List<BaseClass> in a read-only fashion.

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

otherPeople = farmers.Convert All(new Converter<Farme r, 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
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<DerivedCla ss> to a
function that processes a List<BaseClass> in a read-only fashion.

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

otherPeople = farmers.Convert All(new Converter<Farme r, 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
"Jon Skeet [C# MVP]" <sk***@pobox.co m> wrote in message
news:11******** *************@g 44g2000cwa.goog legroups.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

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
25422
by: KC | last post by:
Could some one explain to me the casting rules for sending generic lists, ex. List<Person>, to a function that accepts List<object>? I cannot get the following easy-cheesy app to work. I get the following error: Argument '1': cannot convert from 'GenericsPOC.ArticleCollection' to 'System.Collections.Generic.IList<object>' I tried casting specifically to IList<object> but then I get a runtime error: Unable to cast object of type...
2
2037
by: Jasper Kent | last post by:
I'm trying to do the equivalent of using typedefs with templates in C++ to avoid long instantiation names. I can do this okay: using BigDictionary = System.Collections.Generic.Dictionary<int, System.Collections.Generic.Dictionary<int, string>>; class MyClass {
5
1110
by: lufil | last post by:
How can I make the following code work on beta 2? (I'm not sure but I think it worked on the beta1 version of VS2005) Public Person: IPerson { .... } .... List<IPerson> persons = (List<IPerson>)new List<Person>();
4
53030
by: matty.hall | last post by:
I have two classes: a base class (BaseClass) and a class deriving from it (DerivedClass). I have a List<DerivedClass> that for various reasons needs to be of that type, and not a List<BaseClass>. However, I need to cast that list to a List<BaseClass> and it is not working. The code is below. I get the following exception: "Unable to cast object of type 'System.Collections.Generic.List`1' to type 'System.Collections.Generic.List`1'." ...
5
2925
by: anders.forsgren | last post by:
This is a common problem with generics, but I hope someone has found the best way of solving it. I have these classes: "Fruit" which is a baseclass, and "Apple" which is derived. Further I have an "AppleBasket" which is a class that contains a collection of apples. So, some code: class Fruit{ }
25
3045
by: Lars | last post by:
Hi, I have a base class holding a generic list that needs to be accessed by both the base class and its subclasses. What is the best solution to this? I am fairly new to generics, but I am aware of that fact that if you have a class B, that inherits from A, then List<Bdoes NOT inherit from List<A>. So I understand why the example below does not compile, but I fail to
7
13722
by: S. Lorétan | last post by:
Hi guys, Sorry for this stupid question, but I don't know why it isn't working. Here is my (example) code: namespace Test { class A { public string Label1; }
4
7572
by: =?Utf-8?B?QkogU2FmZGll?= | last post by:
We have a class that has a public property that is of type List<T>. FXCop generates a DoNotExposeGenericLists error, indicating "System.Collections.Generic.List<Tis a generic collection designed for performance not inheritance and, therefore, does not contain any virtual members. The following generic collections are designed for inheritance and should be exposed instead of System.Collections.Generic.List<T>. *...
2
5185
by: Jeff | last post by:
ASP.NET 2.0 In the business logic layer I've got the code (see CODE) below, this code gives a compile error (see ERROR) CODE: List<Messagemessages = null; messages = HttpContext.Current.Session; ERROR:
0
9811
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9657
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10532
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
10577
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
6975
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
5642
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5812
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
4443
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
4003
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.