Home All Groups Group Topic Archive Search About
Author
18 Mar 2006 7:31 PM
Alexander van Doormalen
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.

Author
19 Mar 2006 11:29 AM
Frans Bouma [C# MVP]
Alexander van Doormalen wrote:

Show quoteHide quote
> 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);

    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#)
------------------------------------------------------------------------
Are all your drivers up to date? click for free checkup

Author
19 Mar 2006 1:20 PM
Jon Skeet [C# MVP]
Frans Bouma [C# MVP] <perseus.usenetNOSPAM@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
Author
19 Mar 2006 8:46 PM
Alexander van Doormalen
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!
Author
19 Mar 2006 10:35 PM
Jon Skeet [C# MVP]
Alexander van Doormalen <avdoorma***@gmail.com> wrote:
Show quoteHide quote
> 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.

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

--
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
Author
20 Mar 2006 6:59 AM
Alexander van Doormalen
Will look into that when I've got some time again. Thnx for the
suggestions.
Author
20 Mar 2006 8:48 AM
Frans Bouma [C# MVP]
Alexander van Doormalen wrote:

Show quoteHide quote
> 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.

    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


--
------------------------------------------------------------------------
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#)
------------------------------------------------------------------------
Author
20 Mar 2006 3:17 PM
Alexander van Doormalen
Then I need 2 interfaces as I have both internal and public methods.
Will give it some thought.
Author
20 Mar 2006 4:59 PM
Alexander van Doormalen
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...
Author
20 Mar 2006 6:13 PM
Jon Skeet [C# MVP]
Alexander van Doormalen <avdoorma***@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.

--
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
Author
20 Mar 2006 6:26 PM
Alexander van Doormalen
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
Author
20 Mar 2006 8:45 AM
Frans Bouma [C# MVP]
Jon Skeet [C# MVP] wrote:

Show quoteHide quote
> Frans Bouma [C# MVP] <perseus.usenetNOSPAM@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

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



Post Thread options