如何使用Activator.CreateInstance创建一个List <T>,其中T在运行时未知?

Wil*_*ill 2 c# reflection activator

我正在使用Activator.CreateInstance类型变量创建对象(在运行时未知):

static dynamic CreateFoo( Type t ) =>
    Activator.CreateInstance( t );
Run Code Online (Sandbox Code Playgroud)

显然,我还没有正确理解如何使用该dynamic类型,因为这仍然只是返回一个对象.

我需要能够将一个集合传递给另一个调用,Activator.CreateInstance在那里创建的类型可能是List<T>:

var values = Enumerable.Range( 1, 50 ).Select(
    I => CreateFoo( typeof( Foo ) ) ).ToArray( );
//This will explode.
var foobar = Activator.CreateInstance(
    typeof( List<Foo> ), values );
Run Code Online (Sandbox Code Playgroud)

调用上面的内容时,它会爆炸,但会出现以下异常:

在此输入图像描述

我明白为什么它这样做 - 当列表使用类型参数定义时,没有列表的构造函数期望可枚举的对象.

问题是我无法转换对象,因为我不知道运行时的类型.Activator.CreateInstance只是似乎返回一个对象,这对List<Foo>我来说很好,因为我将使用依赖对象和属性设置它们,所以盒装对象对他们来说完全没问题,但在尝试创建列表时会破坏一切(可能还有什么否则与构造函数期望类型参数).

我想在这里做什么的正确方法是什么?

符合最小,完整和可验证的示例要求:

using System;
using System.Collections.Generic;
using System.Linq;

namespace MCVEConsole {
    class Program {
        static int Main( string[ ] args ) {
            var values = Enumerable.Range( 1, 50 ).Select(
                I => CreateFoo( typeof( Foo ) ) ).ToArray( );

            //This will compile, but explode when run.
            var foobar = Activator.CreateInstance(
                typeof( List<Foo> ), values );
            return 1;
        }

        static dynamic CreateFoo( Type t ) =>
            Activator.CreateInstance( t );
    }

    class Foo {
        public Foo( ) { }
    }
}
Run Code Online (Sandbox Code Playgroud)

Nin*_*rry 16

使用这种方法:

class Program
{
    static void Main(string[] args)
    {
        CreateListFromType(typeof(Foo));
        CreateListFromType(typeof(int));
    }

    static object CreateListFromType(Type t)
    {
        // Create an array of the required type
        Array values = Array.CreateInstance(t, 50);

        // and fill it with values of the required type
        for (int i = 0; i < 50; i++)
        {
            values.SetValue(CreateFooFromType(t), i);
        }

        // Create a list of the required type, passing the values to the constructor
        Type genericListType = typeof(List<>);
        Type concreteListType = genericListType.MakeGenericType(t);

        object list = Activator.CreateInstance(concreteListType, new object[] { values }); 

        // DO something with list which is now an List<t> filled with 50 ts
        return list;
    }


    // Create a value of the required type
    static object CreateFooFromType(Type t)
    {
        return Activator.CreateInstance(t);
    }
}

class Foo
{
    public Foo() { }
}
Run Code Online (Sandbox Code Playgroud)

这种情况下没有必要使用dynamic。我们只能利用object我们创造的价值。非引用类型将存储在objectusing 装箱中。

为了创建List<>类型,我们可以首先获取泛型类型的表示,然后使用该表示来使用该MakeGenericType方法创建具体类型。

请注意您在创建列表的 CreateInstance 调用中犯的错误:

当尝试构造列表时,您需要将值数组作为元素嵌入到对象数组中。因此,它将Activator寻找List<t>一个需要类型为单个参数的构造函数IEnumerable<t>。

按照您编写的方式,它Activator会查找需要 50 个参数的构造函数,每个参数的类型为 t。


使用非通用 IList 接口的较短版本

using System.Collections;

static IList CreateListFromType(Type t)
{
    // Create a list of the required type and cast to IList
    Type genericListType = typeof(List<>);
    Type concreteListType = genericListType.MakeGenericType(t);
    IList list = Activator.CreateInstance(concreteListType) as IList;

    // Add values
    for (int i = 0; i < 50; i++)
    {
        list.Add(CreateFooFromType(t));
    }

    // DO something with list which is now an List<t> filled with 50 ts
    return list;
}
Run Code Online (Sandbox Code Playgroud)

更接近实际用例:动态列表类型

static void Main(string[] args)
{
    CreateListFromType(typeof(List<Foo>));
    CreateListFromType(typeof(ObservableCollection<int>));
}

static IList CreateListFromType(Type listType)
{
    // Check we have a type that implements IList
    Type iListType = typeof(IList);
    if (!listType.GetInterfaces().Contains(iListType))
    {
        throw new ArgumentException("No IList", nameof(listType));
    }

    // Check we have a a generic type parameter and get it
    Type elementType = listType.GenericTypeArguments.FirstOrDefault();
    if (elementType == null)
    {
        throw new ArgumentException("No Element Type", nameof(listType));
    }

    // Create a list of the required type and cast to IList
    IList list = Activator.CreateInstance(listType) as IList;

    // Add values
    for (int i = 0; i < 50; i++)
    {
        list.Add(CreateFooFromType(elementType));
    }

    // DO something with list which is now a filled object of type listType 
    return list;
}
Run Code Online (Sandbox Code Playgroud)


Eri*_*ert 6

显然,我还没有正确理解如何使用动态类型,因为这仍然只是返回一个对象.

是的,你不明白. 只不过dynamic是带着花哨的帽子的对象.返回类型dynamic表示"我返回object,但调用者应该允许对返回的对象进行操作object,这些操作通常不会被允许,并且这些操作将在运行时解析".

如果那不是你的意思,那就不要用了dynamic.

我明白为什么它这样做 - 当列表使用类型参数定义时,没有列表的构造函数期望可枚举的对象.

正确.在你的代码中:

var values = Enumerable.Range( 1, 50 ).Select(
    I => CreateFoo( typeof( Foo ) ) ).ToArray( );
Run Code Online (Sandbox Code Playgroud)

values是dynamic[],这又是刚刚object[]与滑稽的帽子.因此,您正在调用构造函数,List<Foo>而object[]不是IEnumerable<Foo>根据需要调用构造函数.

我想在这里做什么的正确方法是什么?

在适当的做的事情就是停止使用静态类型语言做动态类型的工作,但我怀疑这将是一个非首发.

你有一个object[].你需要的是一个t[]地方t是通过反射提供的类型. 使用Reflection的问题的解决方案几乎总是使用更多的Reflection.那样做吧.不要这样做:

var values = Enumerable.Range( 1, 50 ).Select(
    I => CreateFoo( typeof( Foo ) ) ).ToArray( );
Run Code Online (Sandbox Code Playgroud)

这不足以使用反射.做这个:

object foos = Array.CreateInstance(typeof(Foo), 50);
Run Code Online (Sandbox Code Playgroud)

大.现在你有了一个Foo[50],你可以使用反射再次填充它,或者dynamic使用动态运行时来调度数组索引; 我假设你可以编写该代码.如果你不能,请问另一个问题.

现在你有一个充满Foo[]是IEnumerable<Foo>,然后你就可以传递给List<Foo>构造函数.

为什么你会这样做 - 创建一个Foo[]获得一个IEnumerable<Foo>与我List<Foo>相同的内容Foo[]- 我不知道.对于没有构造函数的参数并直接填充它的更简单的步骤,这似乎是太多的步骤List<Foo>()!但这就是你问的问题,所以这个问题得到了回答.

这听起来很像我们称之为"XY问题".你对如何解决你的问题有一些疯狂的想法,你问的是疯狂的想法,而不是询问问题,所以答案有点疯狂.什么是真正的问题?