Interminable
Interminable

Reputation: 1410

Creating an initialising an array of a reflected type in a single statement

Is it possible to create and initialise an array of a reflected type in .NET in a single statement?

ie the equivalent of doing this, but using reflection:

var myArray = new String[] {"Test"};

I'm after an Array of a specific type, rather than an array of object.

Because Array lacks a constructor that can initialise values, I've been trying to use a generic List. This is what I've got so far, which doesn't work (no matching constructor can be found):

C#:

var myArray = (Activator.CreateInstance(   typeof(List<>).MakeGenericType(typeof(string)),
                                                System.Reflection.BindingFlags.CreateInstance
                                            |   System.Reflection.BindingFlags.Public
                                            |   System.Reflection.BindingFlags.Instance
                                            |   System.Reflection.BindingFlags.OptionalParamBinding,
                                            null,
                                            new[] {
                                                    (new[]   {Activator.CreateInstance( typeof(string),
                                                                                        System.Reflection.BindingFlags.CreateInstance
                                                                                    |   System.Reflection.BindingFlags.Public
                                                                                    |   System.Reflection.BindingFlags.Instance
                                                                                    |   System.Reflection.BindingFlags.OptionalParamBinding,
                                                                                        null,
                                                                                        new[] { "Test".ToCharArray() },
                                                                                        null)
                                                            }).AsEnumerable()
                                                    },
                                            null));

VB.NET:

Dim myArray = Activator.CreateInstance(     GetType(List(Of)).MakeGenericType(GetType(String)),
                                                Reflection.BindingFlags.CreateInstance _
                                            Or  Reflection.BindingFlags.Public _
                                            Or  Reflection.BindingFlags.Instance _
                                            Or  Reflection.BindingFlags.OptionalParamBinding,
                                            Nothing,
                                            {
                                                {Activator.CreateInstance(  GetType(String),
                                                                            Reflection.BindingFlags.CreateInstance _
                                                                        Or  Reflection.BindingFlags.Public _
                                                                        Or  Reflection.BindingFlags.Instance _
                                                                        Or  Reflection.BindingFlags.OptionalParamBinding,
                                                                            Nothing,
                                                                            {"Test".ToCharArray()}, Nothing)
                                                }.AsEnumerable()
                                            },
                                            Nothing)

This question is asked out of curiosity about what is possible, rather than what should be done (as in I don't care if the solution is hideous, this is just for fun). I'll happily accept answers for either language, if this is possible!

Upvotes: 1

Views: 1452

Answers (2)

Jeppe Stig Nielsen
Jeppe Stig Nielsen

Reputation: 61912

It should be easy enough with an array (like mike z's comment):

object entry = "Test";
Type type = typeof(string);

Array myArray = Array.CreateInstance(type, 1);
myArray.SetValue(entry, 0);

It creates an instance of Length one, and then sets the entry at index zero.

It is not easier with a List<>. You can do it like this:

object entry = "Test";
Type type = typeof(string);

object myList = Activator.CreateInstance(typeof(List<>).MakeGenericType(type));
myList.GetType().GetMethod("Add").Invoke(myList, new object[] { entry, });

It creates an instance with the default constructor, and then acquires the Add method and invokes it with an argument "series" { entry, }.

Using the List<> constructor overload that takes an argument called collection to initialize the List<> seems a bit circular. Because to use that, you must already have some "collection" of implementing IEnumerable<string> in advance, to create your List<> from.

Upvotes: 0

Xiaoy312
Xiaoy312

Reputation: 14477

List<T> have a constructor that accepts IEnumerable<T>, but you are passing an IEnumerable<object> to it. Hence why it failed with a MissingMethodException.

You simply need to replace the .AsEnumerable() with .Cast<string>():

var myArray = (Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(string)),
    System.Reflection.BindingFlags.CreateInstance
    | System.Reflection.BindingFlags.Public
    | System.Reflection.BindingFlags.Instance
    | System.Reflection.BindingFlags.OptionalParamBinding,
    null,
    new[] {
        (new[] { Activator.CreateInstance(typeof(string),
        System.Reflection.BindingFlags.CreateInstance
        | System.Reflection.BindingFlags.Public
        | System.Reflection.BindingFlags.Instance
        | System.Reflection.BindingFlags.OptionalParamBinding,
        null,
        new[] { "Test".ToCharArray() },
        null)}).Cast<string>()
    },
    null));

Edit: You can always cast it through reflection:

var myArray = (Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(string)),
    System.Reflection.BindingFlags.CreateInstance
    | System.Reflection.BindingFlags.Public
    | System.Reflection.BindingFlags.Instance
    | System.Reflection.BindingFlags.OptionalParamBinding,
    null,
    new[]
    {
        typeof(Enumerable).GetMethod("Cast")
            .MakeGenericMethod(typeof(string))
            .Invoke(null, new []
            {
                (new[] { Activator.CreateInstance(typeof(string),
                System.Reflection.BindingFlags.CreateInstance
                | System.Reflection.BindingFlags.Public
                | System.Reflection.BindingFlags.Instance
                | System.Reflection.BindingFlags.OptionalParamBinding,
                null,
                new[] { "Test".ToCharArray() },
                null)})
            })
    },
    null));

Upvotes: 2

Related Questions