Search code examples
c#reflectionactivator

How can I use Activator.CreateInstance to create a List<T> where T is unknown at runtime?


I'm using Activator.CreateInstance to create objects by a type variable (unknown during run time):

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

Obviously, I do not yet properly understand how to use the dynamic type, because this is still just returning an object.

I need to be able to pass a collection to another call to Activator.CreateInstance where the type being created could be a 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 );

When the above is called, it explodes with the following exception:

enter image description here

I get why it is doing that - there is no constructor for a list expecting an enumerable of objects when the list is defined with a type argument.

The problem is that I cannot cast the objects because I don't know the type at runtime. Activator.CreateInstance only ever seems to return an object, which is fine for the List<Foo> because I'll be setting them using Dependency Objects and Properties, so boxed objects are perfectly fine for them, but breaks everything when trying to create a list ( and, likely, anything else with a constructor expecting a type argument ).

What is the proper method for what I am trying to get done here?

In compliance with the Minimal, Complete and Verifiable Example requirements:

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( ) { }
    }
}

Solution

  • Use this approach:

    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() { }
    }
    

    There is no need to use dynamic in this case. We can just use object for the value we create. Non-Reference types will be stored in the object using boxing.

    In order to create the List<> type, we can first get a representation of the generic type and then use that to create the concrete type using the MakeGenericType method.

    Note the mistake you made in the CreateInstance call to create the list:

    When trying to construct the list, you need to embed your values array as an element in an array of object. So that Activator will look for a constructor in List<t> that expects a single parameter of type IEnumerable<t>.

    The way you have written it, the Activator looks for a constructor which expects 50 arguments, each of type t.


    A shorter version using non-generic IList interface

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

    Getting closer to the actual use case: Dynamic List Type

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