Передача инициализированного типа System.Type в качестве параметра типа для общего класса

Название немного неясное. Я хочу знать, если это возможно:

string typeName = <read type name from somwhere>;
Type myType = Type.GetType(typeName);

MyGenericClass<myType> myGenericClass = new MyGenericClass<myType>();

Очевидно, MyGenericClass описывается как:

public class MyGenericClass<T>

В настоящий момент компилятор жалуется, что "Тип или пространство имен" myType "не удалось найти." Должен быть способ сделать это.

Ответ 1

Вы не можете сделать это без размышлений. Однако вы можете сделать это с отражением. Вот полный пример:

using System;
using System.Reflection;

public class Generic<T>
{
    public Generic()
    {
        Console.WriteLine("T={0}", typeof(T));
    }
}

class Test
{
    static void Main()
    {
        string typeName = "System.String";
        Type typeArgument = Type.GetType(typeName);

        Type genericClass = typeof(Generic<>);
        // MakeGenericType is badly named
        Type constructedClass = genericClass.MakeGenericType(typeArgument);

        object created = Activator.CreateInstance(constructedClass);
    }
}

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

Type genericClass = typeof(IReadOnlyDictionary<,>);
Type constructedClass = genericClass.MakeGenericType(typeArgument1, typeArgument2);

Ответ 2

К сожалению, нет. Общие аргументы должны быть разрешимы во время компиляции как 1) допустимый тип или 2) другой общий параметр. Невозможно создать общие экземпляры, основанные на значениях времени выполнения, без большого молотка, использующего отражение.

Ответ 3

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

public class Encoder() {
public void Markdown(IEnumerable<FooContent> contents) { do magic }
public void Markdown(IEnumerable<BarContent> contents) { do magic2 }
}

Предположим, что во время выполнения у вас есть FooContent

Если вам удалось привязать во время компиляции, вы хотели бы

var fooContents = new List<FooContent>(fooContent)
new Encoder().Markdown(fooContents)

Однако вы не можете сделать это во время выполнения. Чтобы сделать это во время выполнения, выполните следующие действия:

var listType = typeof(List<>).MakeGenericType(myType);
var dynamicList = Activator.CreateInstance(listType);
((IList)dynamicList).Add(fooContent);

Чтобы динамически вызывать Markdown(IEnumerable<FooContent> contents)

new Encoder().Markdown( (dynamic) dynamicList)

Обратите внимание на использование dynamic в вызове метода. Во время выполнения dynamicList будет List<FooContent> (дополнительно также IEnumerable<FooContent>), так как даже использование динамика все еще привязано к строго типизированному языку, связующее время будет выбирать соответствующий метод Markdown. Если нет точных совпадений типа, он будет искать метод параметров объекта, и если ни одно из них не будет соответствовать исключению связующего времени во время выполнения, будет указано предупреждение о том, что метод не соответствует.

Очевидным недостатком такого подхода является огромная потеря безопасности типа во время компиляции. Тем не менее код в этих строках позволит вам работать в очень динамичном смысле, что во время выполнения все еще полностью набирается так, как вы ожидаете.

Ответ 4

Мои требования были немного разными, но, надеюсь, кто-то поможет. Мне нужно было прочитать тип из конфигурации и динамически создавать типовой тип.

namespace GenericTest
{
    public class Item
    {
    }
}

namespace GenericTest
{
    public class GenericClass<T>
    {
    }
}

Наконец, вот как вы это называете. Определите тип с backtick.

var t = Type.GetType("GenericTest.GenericClass`1[[GenericTest.Item, GenericTest]], GenericTest");
var a = Activator.CreateInstance(t);

Ответ 5

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

public class Type1 { }

public class Type2 { }

public class Generic<T> { }

public class Program
{
    public static void Main()
    {
        var typeName = nameof(Type1);

        switch (typeName)
        {
            case nameof(Type1):
                var type1 = new Generic<Type1>();
                // do something
                break;
            case nameof(Type2):
                var type2 = new Generic<Type2>();
                // do something
                break;
        }
    }
}

Ответ 6

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

void AddValue<T>(object targetList, T valueToAdd)
{
    var addMethod = targetList.GetType().GetMethod("Add");
    addMethod.Invoke(targetList, new[] { valueToAdd } as object[]);
}

var listType = typeof(List<>).MakeGenericType(new[] { dynamicType }); // dynamicType is the type you want
var list = Activator.CreateInstance(listType);

AddValue(list, 5);

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