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;
}
Obviously, I do not yet properly understand how to use the dynamic type, because this is still just returning an object.
Yes, you do not understand it. dynamic
is nothing more than object with a fancy hat on. A return type of dynamic
means "I return object
, but callers should allow operations on the returned object that would not normally be allowed on object
, and those operations will be resolved at runtime".
If that's not precisely what you mean, then do not use dynamic
.
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.
Correct. In your code:
var values = Enumerable.Range( 1, 50 ).Select(
I => CreateFoo( typeof( Foo ) ) ).ToArray( );
values
is dynamic[]
, which again, is just object[]
with a funny hat on. So you are calling the constructor of List<Foo>
with an object[]
, not an IEnumerable<Foo>
as is required.
What is the proper method for what I am trying to get done here?
The proper thing to do is to stop using a statically-typed language to do dynamically-typed work, but I suspect that will be a non-starter.
You have an object[]
in hand. What you want is a t[]
where t
is a type supplied by reflection. The solution to a problem with Reflection is almost always to use more Reflection. So do that. Do not do this:
var values = Enumerable.Range( 1, 50 ).Select(
I => CreateFoo( typeof( Foo ) ) ).ToArray( );
That doesn't use reflection enough. Do this:
object foos = Array.CreateInstance(typeof(Foo), 50);
Great. Now you have a Foo[50]
in hand, and you can fill it again using reflection, or make it dynamic
and use the dynamic runtime to dispatch the array indexing; I assume you can write that code. If you cannot, ask another question.
Now you have a filled Foo[]
which is IEnumerable<Foo>
, which you can then pass to the List<Foo>
constructor.
Why on earth you would do that -- create a Foo[]
to get an IEnumerable<Foo>
to get a List<Foo>
that has the same contents as the Foo[]
-- I do not know. That seems like far too many steps over the much easier step of making a List<Foo>()
with no arguments to the constructor and filling it directly! But that's the question you asked, and so that's the question that got answered.
This sounds a lot like what we call an "XY question". You have some crazy idea about how to solve your problem and you are asking about the crazy idea instead of asking about the problem, and so the answer is a bit crazy itself. What's the real problem?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With