当foo是一个类型变量并且我不知道类型时,如何将List <object>转换为List <foo>

时间:2017-09-29 19:36:45

标签: c# reflection casting

我有一种情况,通过反射我有List&gt;对象&lt;。我还有一个反射方法,它采用List&gt; foo&lt;。列表&gt;&对象LT;包含一大堆foos,但List&gt; object&lt;无法直接投放到List&gt; foo&lt;。我有一个包含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

2 个答案:

答案 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 。这当然,不是你想要的。所以我们需要在T​​oList方法上做同样的事情。把你的血腥细节放在一边,所有这些都应该看起来像这样

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)

不要使用Reflection,使用ArrayList(不,真的)

你还记得旧类型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
    );