У меня очень странная проблема со следующим кодом:

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;
        }
    }
}

Это терпит неудачу от второй до последней строки:

result = Activator.CreateInstance(specificType, values);

Это странно, поскольку оно в основном идентично строке, выполняемой непосредственно перед ним:

result = Activator.CreateInstance(specificType, testArgs);

В обоих случаях один и тот же аргумент передается как параметр specificType, а string[2] передается как второй параметр.

И метод GimeStringTuples работает просто отлично ... хотя там нет никаких обобщений - это может быть намек.

Кто-нибудь может объяснить это необычное поведение?

2
James Crosswell 30 Мар 2017 в 23:08

2 ответа

Лучший ответ

Я могу сказать вам, что происходит. Взглянув на полученную сборку, вы можете увидеть, что компилятор вводит object[], заключающий в себе аргумент values:

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

Теперь правильной перегрузки больше не найти. Если вы добавите приведение, вы получите ожидаемый результат, и код снова заработает:

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

Но я не могу сказать вам, почему это происходит, может быть, это можно найти из спецификации.

1
thehennyy 30 Мар 2017 в 20:48

Итак, решение (спасибо @thehennyy):

using System;
using System.Linq;

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

        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);
            object[] constructorArguments = values.Cast<object>().ToArray();
            return Activator.CreateInstance(specificType, constructorArguments);
        }
    }
}

Это заставляет компилятор передавать элементы общего массива как параметры, а не весь массив как один параметр.

0
James Crosswell 30 Мар 2017 в 21:07