<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.
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#)
------------------------------------------------------------------------
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
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!
Why not still use interfaces, but then cast in the code which "knows
better"? It's not great, but it's better than reflection...
> 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
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
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...
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.
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