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