我有一种情况,通过反射我有List>对象<。我还有一个反射方法,它采用List> foo<。列表>&对象LT;包含一大堆foos,但List> object<无法直接投放到List> foo<。我有一个包含foo的Type变量,但在编译时不知道它。我的代码看起来像下面的
var ASM = Assembly.LoadFrom("foo.dll");
var bar = List<string>() { /*A whole bunch of string literals*/ };
var FooType = ASM.GetType("Foo")
List<object> foos = new List<object>;
foreach (var str in bar)
{
foos.add(Activator.CreateInstance(FooType, new [] {str}));
}
var Flags = BindingFlags.Static | BindingFalgs.Public;
FooType.GetMethod("DoSomethingWithAListOfFoo", Flags).Invoke(null, foos)
此时我被告知无法通过运行时异常将List转换为List
答案 0 :(得分:3)
反思让你陷入困境,所以你需要反思才能让你离开,只有它会更深刻的黑暗反射。对通用方法的反思。
所以,让我们说你已经填充了你的foos变量。这是一个对象列表。你如何将一些东西列入其他东西?投! 在您的实例中,您想要Cast但您无法从我理解的内容中访问它,因此我们需要创建Cast方法的实例。首先,我们需要获得Generic Cast方法
var GenericCastMethod = typeof(System.Linq.Enumerable)
.GetMethod("Cast", BindingFlags.Public | BindingFlags.Static);
现在我们需要制作一个特定于类型的强制转换方法
var SpecificCastMethod = GenericCastMethod.MakeGenericMethod(FooType);
最后,我们需要在您的列表中调用此
var FoosOfTypeFoo = SpecificCastMethod.Invoke(null, new object[] { foos });
您现在可以将FoosOfTypeFoo 用作IEnumerable 。这当然,不是你想要的。所以我们需要在ToList方法上做同样的事情。把你的血腥细节放在一边,所有这些都应该看起来像这样
var GenericCastMethod = typeof(System.Linq.Enumerable)
.GetMethod("Cast", BindingFlags.Public | BindingFlags.Static);
var SpecificCastMethod = GenericCastMethod.MakeGenericMethod(FooType);
var IEnumerableOfFoo = SpecificCastMethod.Invoke(null, new object[] { foos });
var GenericToListMethod = typeof(System.Linq.Enumerable)
.GetMethod("ToList", BindingFlags.Public | BindingFlags.Static);
var SpecificToListMethod = GenericToListMethod .MakeGenericMethod(FooType);
var ListOfFoo = SpecificToListMethod .Invoke(null, new object[] { FoosOfTypeFoo });
这个故事的寓意是,黑魔法代码会产生更黑暗的黑魔法代码。
答案 1 :(得分:1)
你还记得旧类型ArrayList
吗?没有人再用它了;它就像List<object>
一样。但是有一个关键的区别:ArrayList有一个ToArray(Type)
方法(不是ToArray<T>
方法,因为当时泛型不存在)。因此,您可以轻松地创建任何类型的数组,只要您可以为它们获取Type
对象。
以下是两种利用此功能的解决方案。
解决方案1.一行更改。
如果DoSomethingWithAListOfFoos
接受IEnumerable<Foo>
(并且不要求它为List<Foo>
),您可以使用此功能:
FooType.GetMethod("DoSomethingWithAListOfFoo", Flags)
.Invoke(
null,
new ArrayList(foos).ToArray(FooType) //Magic!!!
);
够简单吧?你甚至不需要反思任何事情。
解决方案2.扩展方法。
如果DoSomethingWithAListOfFoos
确实需要List<Foo>
,我们还有一些工作要做,因为我们必须构建通用列表。
首先,在代码中的某处添加此扩展方法:
static public ICollection ToGenericList(this ICollection input, Type itemType)
{
return (ICollection) typeof(List<>)
.MakeGenericType(itemType)
.InvokeMember(
null,
BindingFlags.CreateInstance,
null,
null,
new object[] { new ArrayList(input).ToArray(itemType) }
);
}
这是有效的,因为每个List<T>
都带有一个接受数组的构造函数。我们已经弄清楚如何轻松制作阵列。我们只是找到正确的构造函数并将其传入,运行时完成其余的工作。
现在就这样称呼:
FooType.GetMethod("DoSomethingWithAListOfFoo", Flags)
.Invoke(
null,
new foos.ToGenericList(FooType) //Our new extension method
);