Search code examples
genericsreflectionactivator

Issue activating a generic type with generic array in constructor parameters


I have a very strange issue with the following code:

using System;
using System.Linq;

namespace ConsoleApp1
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var testTuples1 = GimeStringTuples("foo", "bar");
            var testTuples2 = GimeTuples("foo", "bar");
            Console.ReadKey();
        }

        public static object GimeStringTuples(params string[] values)
        {
            Type genericType = Type.GetType("System.Tuple`" + values.Length);
            Type[] typeArgs = values.Select(_ => typeof(string)).ToArray();
            Type specificType = genericType.MakeGenericType(typeArgs);
            return Activator.CreateInstance(specificType, values);
        }

        public static object GimeTuples<T>(params T[] values)
        {
            Type genericType = Type.GetType("System.Tuple`" + values.Length);
            Type[] typeArgs = values.Select(_ => typeof(T)).ToArray();
            Type specificType = genericType.MakeGenericType(typeArgs);

            dynamic result;
            string[] testArgs = { "foo", "bar" };
            result = Activator.CreateInstance(specificType, testArgs);
            result = Activator.CreateInstance(specificType, values);
            return result;
        }
    }
}

It's failing on the second to last line:

result = Activator.CreateInstance(specificType, values);

This is weird since it's basically identical to the line that executes just before it:

result = Activator.CreateInstance(specificType, testArgs);

In both cases, the same argument is being passed as the specificType parameter and a string[2] is being passed as the second parameter.

And the GimeStringTuples method works just fine... although there are no generics involved there - which is maybe the hint.

Can anyone explain this unusual behaviour?


Solution

  • I can tell you what happens. Having a look at the resulting assembly you can see that the compiler introduces an object[] wrapping the values argument:

    Activator.CreateInstance(specificType, new string[] { "foo", "bar" });
    Activator.CreateInstance(specificType, new object[] { values });
    

    Now the right overload can not be found anymore. If you add a cast you will get the expected result and the code works again:

    Activator.CreateInstance(specificType, values as string[])
    

    But i can not tell you why this happens, maybe it can be digged out of the specs.