Filipe Scur
Filipe Scur

Reputation: 133

Reflection over inherited generic types

I'm facing a problem with reflection in c# and I can't find the answer.

I have a class that inherits from a generic type, and I'm trying to retrieve the type of T from this class, but it turns out that I can't!

Here is a example:

class Products : List<Product>
{}

The problem is that at runtime I don't know the type of T. So I tried to get the type like this:

Type itemsType = destObject.GetType().GetGenericArguments()[0]

It didn't work out.

Here is my method:

public static object Deserialize(Type destType, XmlNode xmlNode)
    {         
        object destObject = Activator.CreateInstance(destType);

        foreach (PropertyInfo property in destType.GetProperties())
            foreach (object att in property.GetCustomAttributes(false))
                if (att is XmlAttributeAttribute)
                    property.SetValue(destObject, xmlNode.Attributes[property.Name].Value, null);
                else if (att is XmlNodeAttribute)
                {
                    object retObject = Deserialize(property.PropertyType, xmlNode.Nodes[property.Name]);
                    property.SetValue(destObject, retObject, null);
                }

        if (destObject is IList)
        {
            Type itemsType = destObject.GetType().GetGenericArguments()[0];
            foreach (XmlNode xmlChildNode in xmlNode.Nodes)
            {
                object retObject = Deserialize(itemsType, xmlNode);
                ((IList)destObject).Add(retObject);
            }
        }

        return destObject;
    }        

The idea is to read an xml file and transform it in an object:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SETTINGS>
  <PRODUCTS>
    <PRODUCT NAME="ANY" VERSION="ANY" ISCURRENT="TRUE" />
    <PRODUCT NAME="TEST1" VERSION="ANY" ISCURRENT="FALSE" />
    <PRODUCT NAME="TEST2" VERSION="ANY" ISCURRENT="FALSE" />
  </PRODUCTS>
  <DISTRIBUTIONS>
    <DISTRIBUTION NAME="5.32.22" />
  </DISTRIBUTIONS>
</SETTINGS>

in this case the node PRODUCTS would be my collection that inherits from List

any ideas on how to do this?

tks guys

Upvotes: 4

Views: 2487

Answers (2)

Gabe
Gabe

Reputation: 86718

If you're just trying to figure out what the type of an IList is, you should use something like this:

Type itemsType = destType.GetInterface(typeof(IList<>).Name).GetGenericArguments()[0];

Here's how you would use it in code:

var interface = destType.GetInterface(typeof(IList<>).Name);
var destList = destObject as IList;
// make sure that the destination is both IList and IList<T>
if (interface != null && destList != null)
{
    Type itemsType = interface.GetGenericArguments()[0];
    foreach (XmlNode xmlChildNode in xmlNode.Nodes) 
    { 
        object retObject = Deserialize(itemsType, xmlNode); 
        destList.Add(retObject); 
    } 
} 

Upvotes: 1

SLaks
SLaks

Reputation: 887423

The Products class isn't generic, so GetGenericArguments doesn't return anything.

You need to get the generic arguments of the base type, like this:

Type itemType = destObject.GetType().BaseType.GetGenericArguments()[0];

However, this is not resilient; if an intermediary non-generic base type is introduced, it will fail.
Instead, you should find the type parameter of the IList<T> implementation.

For example:

Type listImplementation = destObject.GetType().GetInterface(typeof(IList<>).Name);
if (listImplementation != null) {
    Type itemType = listImplementation.GetGenericArguments()[0];
    ...
}

Upvotes: 6

Related Questions