C# Dynamic Cast with Reflection and Lists

2019-07-19 00:40发布

问题:

since yesterday i'm working on a problem and i don't get it yet...

I've got a class with many Methods and decide in Runtime wich Method has to be called. Every of this Methods returns a List with Elements from my Businessobjects.

My Class looks this way:

public class ReflectiveClass {

    public List<BO1> DoSomethingWithBO1(int param){
        List<BO1> list = new List<BO1>();
        //....
        return list;
    }

    public List<BO2> DoSomethingWithBO2(int param){
        List<BO2> list = new List<BO2>();
        //....
        return list;
    }

    public void Process(){
        //...get MethodInfo and so on
        List<object> myReturnValue = (List<object>)methodInfo.Invoke(this, new object[]{param});
        // here comes the Exception
    }
}

So, at Invoking the Method i got a InvalidCastException and the Debugger told me he could not Cast from

System.Collections.Generic.List`1[BO1]

to

System.Collections.Generic.List`1[System.Object]

I wonder why this doesn't work. I thougt if i use a List every Object could be in this List.

I've even tried it with List but same behaviour.

Is it possible to read reflective the Type of the Return-Value of a Method? And can i then create a Generic List with this Returnvalue and cast to this List? This would be wonderfull.

Greetings and many Thanks for your Help! Benni

回答1:

Obviously BO1 derives from Object, and you can't cast List<Derived> to List<Base>. Suppose we have:

List<Apple> apples = AListOfApples();
List<Fruit> fruits = (List<Fruit>)apples;  //suppose it's valid to cast
fruits.Add(new Orange());  //Of course we can add an Orange to the list of Fruit
//Now you can see the list of Apple has an Orange in it!!

You can use IEnumerable<T> instead.



回答2:

If you have behaviour that changes and is determined at runtime, it's ideal for the Strategy pattern. Have a look at http://www.dofactory.com/Patterns/PatternStrategy.aspx



回答3:

List<_> needs to be invariant to be statically type-safe. Imagine this compiled

var strlist = List<string> { "blub" };
var olist = (List<object>)strlist;

Up to this point everything is nice and dandy, but if you now tried to write to the list like so

olist.Add(3);

the runtime would have to throw an exception as the underlying array is not an int array, but a string array. That's why it does not compile in the first place.

Note that contary to generic lists, arrays have been covariant since C# 1.0, probably for Java compatibility. So this indeed compiles:

string[] strlist = new[] { "huhu" };
var olist = (object[])strlist;
olist[0] = 3;

... but throws an exception at runtime.

IEnumerable<out T> is covariant in T in C# 4.0 (therefore the out). Maybe this would be the more appropriate interface for your purposes.



回答4:

You can use this :

object myReturnValue = mi.Invoke(this, new object[] { });
MethodInfo miToList = typeof(Enumerable).GetMethod("ToList");
MethodInfo miListObject = miToList.MakeGenericMethod(new[] { typeof(object) });
List<object> listObject = (List<object>)miListObject.Invoke(myReturnValue, new object [] { myReturnValue });


回答5:

You should really split your class into two different classes, that should implement same interface. Using reflaction here is not a good thing.

Or if you methdods differ only in type of input parameters, make them generic.



回答6:

Well the only solution is to create a new list..

 public void Process(){
        //...get MethodInfo and so on
        List<object> myReturnValue = new List<object>(((IList)methodInfo.Invoke(this, new object[]{param})).ToArray());
        // here comes no Exception!
    }


回答7:

I appreciate all the Answers!

For your information: I've implemented the Strategy Pattern, because it fits really good to my Project.

PS: I love this community, the peoble here help you so quick and with good solutions. Thanks!