Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Generics and casting

0 views
Skip to first unread message

Alexander van Doormalen

unread,
Mar 18, 2006, 2:31:14 PM3/18/06
to
I have a xml file with data from various sources. For example:

<root>
<Account>
<Id>1</Id>
<Name>Somename</Name>
<City>Somecity</City>
<ContactPersons>
<ContactPerson>
<Id>1</Id>
<Name>Somename</Name>
</ContactPerson>
<ContactPerson>
<Id>2</Id>
<Name>Somename2</Name>
</ContactPerson>
</ContactPersons>
</Account>
</root>

I created a generic businesscollection (named BusinessCollection) and a
business class (named BusinessObject). Then I created a specific
business class named Account en another one called ContactPerson. They
both derive from the BusinessObject class.

I created a instance of a businesscollection with accounts using:
BusinessCollection <Account> collection = new BusinessCollection
<Account>();

I also made a parser that 'translates' the xml into the correct
business classes. So in case of the data above I provide a instance of
BusinessCollection <Account>. Then I create Account objects and add
them to the collection. This is all done using reflection and some
XPath classes.

The parser has the folling method signature:
public static void ParseToBusinessObjects <T>(BusinessCollection <T>
collection, XPathDocument document) where T : BusinessObject

So far so good. But now I also want subcollections to parse. In the xml
data ContactPersons will be my subcollection (property is named the
same). If my parser detects a subcollection it will get the current
instance (using reflection). I then want to call my entry method again
for this collection (recursive)

However when I do this:
BusinessCollection <BusinessObject> subCollection = (BusinessCollection
<BusinessObject>) property.GetValue(instance, null);

I get a InvalidCastException saying I can't convert between a
BusinessCollection<ContactPerson> and a
BusinessCollection<BusinessObject>.

Its the same when I change it to :
BusinessCollection <T> subCollection = (BusinessCollection <T>)
property.GetValue(instance, null);


I hope someone knows how to solve this because its driving me nuts.

Frans Bouma [C# MVP]

unread,
Mar 19, 2006, 6:29:11 AM3/19/06
to
Alexander van Doormalen wrote:

This isn't supported. It's called covariance, and that's not supported
in .NET generics. BusinessCollection<BusinessObject> isn't a supertype
of BusinessCollection<Contact>, even though it might look like it.
That's why casts fail.

You can solve it easily though. Implement an interface. Interfaces are
a general way to do generic programming if the generic type descriptor
changes. So, on your BusinessCollecttion<T>, you implement
IBusinessCollection. That's a non-generic interface, with the same
methods.

You can now do:
IBusinessCollection subCollection =
(IBusinessCollection)property.Getvalue(instance, null);

and call your code again.

Frans


--
------------------------------------------------------------------------
Lead developer of LLBLGen Pro, the productive O/R mapper for .NET
LLBLGen Pro website: http://www.llblgen.com
My .NET blog: http://weblogs.asp.net/fbouma
Microsoft MVP (C#)
------------------------------------------------------------------------

Jon Skeet [C# MVP]

unread,
Mar 19, 2006, 8:20:13 AM3/19/06
to
Frans Bouma [C# MVP] <perseus.us...@xs4all.nl> wrote:
> > Its the same when I change it to :
> > BusinessCollection <T> subCollection = (BusinessCollection <T>)
> > property.GetValue(instance, null);
>
> This isn't supported. It's called covariance, and that's not supported
> in .NET generics. BusinessCollection<BusinessObject> isn't a supertype
> of BusinessCollection<Contact>, even though it might look like it.
> That's why casts fail.

Just a tiny correction - it's not supported in C# generics. Apparently
.NET itself supports both covariance and contravariance, but C# doesn't
expose it. See the following link for details - it's fascinating
stuff...

http://blogs.msdn.com/rmbyers/archive/2005/02/16/375079.aspx

--
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

Alexander van Doormalen

unread,
Mar 19, 2006, 3:46:17 PM3/19/06
to
To bad thats not possible yet :(. I hope it will be in the feature.

Since using interfaces isn't possibe because of various reasons (like
internal methods that need to be called on the object etc). So I
'solved' it by creating an internal method that expects a plain object.
I then use reflection to call the Add method on that collection (which
is passed as an object)

MethodInfo info = collection.GetType().GetMethod("Add");
object[] paramaters = new object[1];
paramaters[0] = businessObject;

info.Invoke(collection, paramaters);

Not a very clean way but it works for now.

Thnx guys for the help!

Jon Skeet [C# MVP]

unread,
Mar 19, 2006, 5:35:34 PM3/19/06
to

Why not still use interfaces, but then cast in the code which "knows
better"? It's not great, but it's better than reflection...

Alexander van Doormalen

unread,
Mar 20, 2006, 1:59:47 AM3/20/06
to
Will look into that when I've got some time again. Thnx for the
suggestions.

Frans Bouma [C# MVP]

unread,
Mar 20, 2006, 3:45:25 AM3/20/06
to
Jon Skeet [C# MVP] wrote:

> Frans Bouma [C# MVP] <perseus.us...@xs4all.nl> wrote:
> > > Its the same when I change it to :
> > > BusinessCollection <T> subCollection = (BusinessCollection <T>)
> > > property.GetValue(instance, null);
> >
> > This isn't supported. It's called covariance, and that's not
> > supported in .NET generics. BusinessCollection<BusinessObject>
> > isn't a supertype of BusinessCollection<Contact>, even though it
> > might look like it. That's why casts fail.
>
> Just a tiny correction - it's not supported in C# generics.
> Apparently .NET itself supports both covariance and contravariance,
> but C# doesn't expose it. See the following link for details - it's
> fascinating stuff...
>
> http://blogs.msdn.com/rmbyers/archive/2005/02/16/375079.aspx

Thanks, Jon :)

FB

Frans Bouma [C# MVP]

unread,
Mar 20, 2006, 3:48:10 AM3/20/06
to
Alexander van Doormalen wrote:

You can also use an internal interface and implement it explicitly.
Then, cast to the interface as Jon explained. I do that too for
internal methods on my generic collections, works like a charm.

so:
internal interface IFoo
{
void MyInternalMethod();
}

public class BusinessCollection<T>: IFoo
{
void IFoo.MyInternalMethod()
{
//...
}
}


IFoo foo = (IFoo)myCollection;
foo.MyInternalMethod();

FB

Alexander van Doormalen

unread,
Mar 20, 2006, 10:17:11 AM3/20/06
to
Then I need 2 interfaces as I have both internal and public methods.
Will give it some thought.

Alexander van Doormalen

unread,
Mar 20, 2006, 11:59:28 AM3/20/06
to
I added some interfaces but still the same casting problem.

Unable to cast object of type 'BusinessCollection`1[ContactPerson]' to
type 'IBusinessCollection`1[IBusinessObject]'.

OR

Unable to cast object of type 'BusinessCollection`1[ContactPerson]' to
type 'BusinessCollection`1[IBusinessObject]'.

Some details:
- public interface IBusinessCollection<T> where T: IBusinessObject
- public interface IBusinessObject

- public class BusinessCollection<T> : Collection <T>, ICollection <T>,
IBusinessCollection<T> where T: IBusinessObject
- public class User: BusinessObject, IBusinessObject
- public class ContactPerson: BusinessObject, IBusinessObject

- Property inside User class:
public BusinessCollection <ContactPerson> ContactPersons
{
get
{
return this.contactPersons;
}
}

Am I doing something completely wrong or...

Jon Skeet [C# MVP]

unread,
Mar 20, 2006, 1:13:57 PM3/20/06
to
Alexander van Doormalen <avdoo...@gmail.com> wrote:
> I added some interfaces but still the same casting problem.
>
> Unable to cast object of type 'BusinessCollection`1[ContactPerson]' to
> type 'IBusinessCollection`1[IBusinessObject]'.
>
> OR
>
> Unable to cast object of type 'BusinessCollection`1[ContactPerson]' to
> type 'BusinessCollection`1[IBusinessObject]'.

I was talking about casting (on a single element - not the collection)
whenever you needed to use a member of the concrete class which isn't
present in the interface.

Alexander van Doormalen

unread,
Mar 20, 2006, 1:26:21 PM3/20/06
to
Ok my mistake. I though you guys ment using an interface element would
solve the inherritance problem. But you where only focussing on the
reflection call :)

Then I will just go back to passing it as an object to the method
instead of a businesscollection type. Only without the reflection
method call.

Thnx! This will do I guess

0 new messages