0

Have a very interesting situation and can't seem to find anything concrete on the webs surrounding reflection, inheritance and generics.

To start off with:

I have a base abstract classed, called:

public abstract class ItemBase
{
     // default properties
}

ItemBase then becomes my super-class to an abstract entity called Item:

public abstract class Item
{
    // some extra properties
}

The idea behind this is to construct entities that will inherit from Item with additional properties set:

public partial class City : Item {}

Now, in a test method I'm trying to use reflection to invoke a method on the passed entity and return whatever data, in what ever form, back from the method.

Assuming, for testing purposes we have a method in State, that returns a collection of all major cities:

public List<City> GetAllMajorCities() {}

In my stubs I might have a test to get all cities:

List<Item> cities = this.InvokeGenericMethod<Item>("State", "GetAllMajorCities", args);

Then the InvokeGenericMethod(string, string, object[] args) looks something like:

public IEnumerable<T> InvokeGenericMethod<Item>(string className, string methodName, object[] args)
{
     Type classType = className.GetType(); // to get the class to create an instance of
     object classObj = Activator.CreateInstance(classType);
     object ret = classType.InvokeMember(methodName, BindingFlags.InvokeMethod, null, classObj, args);
}

In theory, from the example given, the value of "ret" should be a type of IEnumerable, however, the returning type, if added to a watch to investivage, return List, if I check the ret value as:

if (ret is IEnumerable<T>)

it ignores the check.

If I change

public List<City> GetAllMajorCities() {}

to

public List<ItemBase> GetAllMajorCities() {}

It seems to adhere to the inheritance and allow me to cast it to IEnumberable.

I'm completely stumped here.

Any ideas, recommendations would be greatly appreciated.

Thanks,

Eric

2
  • Can you please recheck your code? I think there a things mixed up. I guess your InvokeGenericMethod will not work. className.GetType() will allways return type string, for example. Commented Apr 20, 2011 at 7:23
  • Hi Achim, the code above is an example of the actual apologies if it's a bit confusing. I use reflection to the the full name of the class, namespace of the class in string format when binding a drop down list, this is then passed, as a string to my InvokeGenericMethod. Commented Apr 20, 2011 at 10:09

3 Answers 3

3

Is it possible that you are using Framework 3.5? Generic co- and contravariance was introduced in .net 4.0. Thus, the following is not possible in Framework 3.5:

class ItemBase { }
class Item : ItemBase { }
class City : Item { }

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<City> cities = new List<City>();
        IEnumerable<Item> items = cities;  // Compile error here. IEnumerable<Item> is not a supertype of IEnumerable<City>.
    }
}

Note that the following (which is what you are trying to achieve) won't even work in .net 4.0:

List<Item> cities = new List<City>();

Why doesn't that work? Because it must be possible to add arbitrary Item s to a List<Item>. But, obviously, cities.Add(new Person()); cannot work (since the "real" (= static) type of cities is List<City>). Thus, List<Item> can never be a supertype of List<City>.

You cannot add items to an IEnumerable, which is why there is a subtype relationship between IEnumerable<City> and IEnumerable<Item> (but only in .net 4.0, as mentioned above).

Sign up to request clarification or add additional context in comments.

1 Comment

Hi Heinzi. Thanks for you explanation, which makes sense. The understanding, falsely that I've been under was because we have a super-class of ItemBase that the sub-classes would automatically be defined as such when reflected. A bit of an oversight on my part.
1

You could try something like this:

        Type type = ret.GetType();
        Type listType = typeof(IEnumerable<T>);
        if (type == listType || type.IsSubclassOf(listType))
        {
            //
        }

Comments

1

First of all, this is wrong:

Type classType = className.GetType();

This construct will yield the type of className which is String but not the type whose name is stored in className.

A better, more type-safe way would be:

public IEnumerable<TItem> InvokeGenericMethod<TItem, TContainer>(...)
    where TItem : ItemBase
    where TContainer : class, new()
{
    TContainer container = new TContainer();
    ...
}

Update: In case the TContainer type is not known statically at the place where InvokeGenericMethod is called, have a look on of the the static Type.GetType methods to resolve the actual name from its string name. In the simplest case the Type.GetType(String) method access an assembly qualified name of your type, e.g. "MyNamespace.City, MyAssembly".

2 Comments

Hi Ondrej, your proposal will work in a situation where the actual Class is know in it's "purist" form, however, I have a drop down box in my control, that lists the name of entities (classes) as string value. I need to get the underlying type of the associated string from the assembly. I will investigate this further as I feel that your proposed solution might have merit if I change my logic.
@JadedEric Undestood and updated my answer accordingly. However, if you can change your logic towards a more type-safe solution, possibly avoiding reflection at all, you will typically benefit from it in the future.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.