9

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( ) { }
    }
}
Will
  • 3,413
  • 7
  • 50
  • 107
  • 1
    Is there any reason why you aren't just creating the list and then adding the items separately? – Federico Dipuma Sep 11 '18 at 22:25
  • @FedericoDipuma Because I don't have access to the list type during run time. It's kept within the `PropertyType` property of a `DependencyProperty` object. So one property could be of type `List` and another could be of type `ObservableCollection`. – Will Sep 11 '18 at 22:27
  • 1
    I still do not understand, why can't you just use `dynamic list = Activator.CreateInstance(yourListType)` and then `foreach (var item in values) list.Add(item);`? – Federico Dipuma Sep 11 '18 at 22:29
  • @FedericoDipuma I guess 'cause I didn't think that would work since the object types weren't know. I figured it would just explode saying "Cannot add type of object to list of ". I will try this. Thanks. – Will Sep 11 '18 at 22:33
  • The `values` variable in your example is a `dynamic[]`, not an array of objects. If you have a different collection you'll better update your question. – Federico Dipuma Sep 11 '18 at 22:34
  • @FedericoDipuma No, I went through the code and changed everything to return dynamic before I asked this question, so your suggestion works. Can't believe it was that simple... – Will Sep 11 '18 at 22:40
  • Every instance of using dynamic means to prevent the compiler from helping you against making mistakes. Dynamic should be used as little as possible. – NineBerry Sep 11 '18 at 23:11
  • @NineBerry Sure. Like I said, "Obviously, I do not yet properly understand...". If I knew what I was doing here I wouldn't have needed to ask the question. Thanks again for the working answer. – Will Sep 11 '18 at 23:15

2 Answers2

16

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;
}
NineBerry
  • 26,306
  • 3
  • 62
  • 93
  • This is perfect. Spot on. Thanks. – Will Sep 11 '18 at 23:09
  • @Will I have updated the question with a bit shorter version using the non-generic IList interface. Basically all lists and collections in C# (like List<> and ObservableCollection<>) implement IList. Then there is also a variant where the list type itself is passed as a parameter. – NineBerry Sep 11 '18 at 23:59
6

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?

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • I guess the 'Real Problem' is that I am trying to create objects based on types without being able to cast those objects into their real forms because I am working with types ( I can't say `List b = ( List )someObject` because I don't have access to knowing `List`, because it is a type stored within a `DependencyProperty`. I am trying to populate the dependency properties of a `DependencyObject` using data from an XML file which is formed by reading data from a `DependencyObject` which has a collection of properties defined within it for use in creating said XML file. – Will Sep 11 '18 at 22:48
  • So, no - your answer isn't crazy, I think it may be exactly what I need. I just did not want to go too far into what I was doing because I felt it would break the `Minimal, Complete and Verifiable Example` requirements for my question. – Will Sep 11 '18 at 22:50
  • 1
    @Will: And now you know why I think that dependency injection is a five dollar word for a five cent idea! You get a small amount of increased flexibility, and the only price you pay is *losing static typing*, *having to write awful reflection code everywhere*, *losing huge amounts of performance*, and *turning problems that could be debugged in the C# debugger into problems that can only be debugged by reading XML fies*. Why anyone thinks this is a good trade is beyond me. – Eric Lippert Sep 11 '18 at 22:51
  • @EricLippert: I prefer to idealize dependency injection as being used to inject an implementation into an interface within source code. This is the approach .Net MVC takes. This avoids many of the trade-offs you list in your example. However, I admit that you still lose static typing when working with things like resx files. A resx designer file provides static typing, but an injected localization service does translation lookups via a generic localization interface (i.e., a string index). Thus, I'm OK with tight coupling between my application and my localization files. – Brian Sep 12 '18 at 20:49
  • @EricLippert I think you meant "IoC containers". Using them anywhere outside of `Main()` is exactly as horrible as you've described — and using them inside `Main()` is also somewhat questionable because `Main()` is the place *where you know the actual types* (well, the types of actual factories, but still), so you can wire it all manually and keep static typing. – Joker_vD Sep 12 '18 at 20:49
  • Obviously, the approach I describe above also tends to force a recompile when the DI types are changed. – Brian Sep 12 '18 at 20:49