C#反射和列表中的动态转换

3

自从昨天以来,我一直在解决一个问题,但我还没有弄明白...

我有一个类,其中有许多方法,我决定在运行时调用哪个方法。每个方法都返回一个包含我的业务对象元素的列表。

我的类看起来像这样:

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
    }
}

因此,在调用该方法时,我遇到了一个 InvalidCastException 错误,并且调试器告诉我无法进行类型转换。
System.Collections.Generic.List`1[BO1]

为了

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

我想知道为什么这个不起作用。我认为如果使用List,每个对象都可以在这个List中。

我甚至尝试了使用List,但结果相同。

是否有可能反射读取方法返回值的类型?然后我能否使用此返回值创建一个泛型列表并将其转换为此列表?这将非常好。

问候和感谢您的帮助! Benni

7个回答

4
显然,BO1来源于Object,您不能将List<Derived>强制转换为List<Base>。假设我们有:
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!!

您可以使用 IEnumerable<T> 替代。

2

List<_>需要是不变的才能在静态类型安全方面起作用。想象一下这个编译后的

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

到目前为止,一切都很好,但是如果您现在尝试这样写入列表

olist.Add(3);

运行时必须抛出异常,因为底层数组不是一个int数组,而是一个字符串数组。这就是为什么它首先无法编译。

请注意,与通用列表相反,自C# 1.0以来,数组一直具有协变性,可能是为了兼容Java。因此,以下代码确实可以编译:

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

... 但在运行时会抛出异常。

C# 4.0中的IEnumerable<out T>T方面是协变的(因此使用了out)。也许这个接口更适合您的目的。


2
您可以使用以下内容:
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 });

2

1

唯一的解决方案就是创建一个新列表。

 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!
    }

1
你应该将你的类分成两个不同的类,它们应该实现相同的接口。在这里使用反射并不是一个好的选择。
或者,如果你的方法只是输入参数类型不同,那么可以将它们定义为泛型方法。

嗨,将其更改为通用类型是不可能的,因为这些方法完全执行不同的操作。 - Benni

0

非常感谢所有的回答!

供您参考:我已经实现了策略模式,因为它非常适合我的项目。

PS:我喜欢这个社区,这里的人们能够快速提供好的解决方案。谢谢!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接