Как я могу использовать Activator.CreateInstance для создания списка <T>, где T неизвестно во время выполнения? - PullRequest
0 голосов
/ 12 сентября 2018

Я использую Activator.CreateInstance для создания объектов с помощью переменной типа (неизвестно во время выполнения):

static dynamic CreateFoo( Type t ) =>
    Activator.CreateInstance( t );

Очевидно, я еще не совсем правильно понимаю, как использовать тип dynamic,потому что это все еще только возвращает объект.

Мне нужно иметь возможность передать коллекцию другому вызову Activator.CreateInstance, где создаваемый тип может быть 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 );

Когда вызывается вышеприведенное, оно взрывается со следующим исключением:

enter image description here

Я понимаю, почему это происходит - для конструктора нет конструктораlist, ожидающий перечисление объектов, когда список определен с аргументом типа.

Проблема в том, что я не могу приводить объекты, потому что я не знаю тип во время выполнения.Activator.CreateInstance кажется, только когда-либо возвращает объект, что хорошо для List<Foo>, потому что я буду устанавливать их, используя объекты и свойства зависимости, поэтому объекты в штучной упаковке отлично подходят для них, но ломает все, когда пытается создать список(и, вероятно, что-нибудь еще с конструктором, ожидающим аргумент типа).

Каков правильный метод для того, что я пытаюсь сделать здесь?

В соответствии с Минимальный, полный и проверяемый пример требования:

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( ) { }
    }
}

Ответы [ 2 ]

0 голосов
/ 12 сентября 2018

Используйте этот подход:

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() { }
}

В этом случае нет необходимости использовать dynamic.Мы можем просто использовать object для значения, которое мы создаем.Не ссылочные типы будут храниться в object с использованием бокса.

Чтобы создать тип List<>, мы можем сначала получить представление универсального типа, а затем использовать его для создания конкретного типа.используя метод MakeGenericType.

Обратите внимание на ошибку, которую вы допустили при вызове CreateInstance для создания списка:

При попытке создать список вам необходимо встроить массив значений в качестве элементав массиве объекта.Так что Activator будет искать конструктор в List<t>, который ожидает один параметр типа IEnumerable<t>.

Как вы его написали, Activator ищет конструктор, который ожидает 50 аргументовкаждый тип t.


более короткая версия с использованием неуниверсального интерфейса IList

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;
}
0 голосов
/ 12 сентября 2018

Очевидно, я еще не совсем правильно понимаю, как использовать динамический тип, потому что он все еще просто возвращает объект.

Да, вы этого не понимаете. dynamic - это не что иное, как объект с необычной шляпой на . Возвращаемый тип dynamic означает «Я возвращаю object, но вызывающие стороны должны разрешить операции с возвращаемым объектом, которые обычно не допускаются на object, и эти операции будут разрешены во время выполнения».

Если это не совсем то, что вы имеете в виду, тогда не используйте dynamic.

Я понимаю, почему он это делает - нет конструктора для списка, ожидающего перечисление объектов, когда список определен с аргументом типа.

Правильно. В вашем коде:

var values = Enumerable.Range( 1, 50 ).Select(
    I => CreateFoo( typeof( Foo ) ) ).ToArray( );

values - это dynamic[], что опять же просто object[] в смешной шапке. Таким образом, вы вызываете конструктор List<Foo> с object[], а не с IEnumerable<Foo>, как требуется.

Каков правильный метод для того, что я пытаюсь сделать здесь?

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

У вас есть object[] в руке. То, что вы хотите - это t[], где t - это тип, предоставленный отражением. Решением проблемы с отражением почти всегда является использование большего количества отражений . Так сделай это. Не делайте этого:

var values = Enumerable.Range( 1, 50 ).Select(
    I => CreateFoo( typeof( Foo ) ) ).ToArray( );

Это недостаточно использует отражение. Сделайте это:

object foos = Array.CreateInstance(typeof(Foo), 50);

Отлично. Теперь у вас есть Foo[50] в руке, и вы можете снова заполнить его , используя отражение , или сделать его dynamic и использовать динамическую среду выполнения для отправки индексации массива; Я полагаю, вы можете написать этот код. Если не можете, задайте другой вопрос.

Теперь у вас есть заполненный Foo[], равный IEnumerable<Foo>, который вы затем можете передать конструктору List<Foo>.

С какой стати вы это сделали - создайте Foo[], чтобы получить IEnumerable<Foo>, чтобы получить List<Foo>, который имеет то же содержимое, что и Foo[] - я не знаю. Кажется, что слишком много шагов над гораздо более простым шагом - сделать List<Foo>() без аргументов для конструктора и заполнить его напрямую! Но это вопрос, который вы задали, и на этот вопрос был дан ответ.

Это очень похоже на то, что мы называем «вопросом XY». У вас есть сумасшедшая идея о том, как решить вашу проблему, и вы спрашиваете о сумасшедшей идее, а не спрашиваете о проблеме, и поэтому сам ответ немного сумасшедший. В чем реальная проблема?

...