2016-12-06 4 views
3

У меня есть объект некоторого типа, известный во время выполнения, и я читаю и десериализую этот объект из базы данных. Оно работает. Теперь я хотел бы добавить его в список:Создать общий родовой объект во время выполнения

private static List<T> generateList<T>() 
{ 
    List<T> lst = new List<T>(); 
    return lst; 
} 

private void readObjects(System.Type objType) 
{ 
    var methodInfo = typeof(My.Serializator).GetMethod("DeserializeDb"); 
    var genericMethod = methodInfo.MakeGenericMethod(objType1); 
    List<curType> currentList= generateList<curType>(); 
    // ...read stream from database and convert it to object 
    while (_rs.Read()) 
    { 
     var _objItem = genericMethod.Invoke(null, new[] { _streamedData }); 
     currentList.Add(_objItem); 
    } 
} 

Это не сработает. Ошибка:

curType - переменная, но используется как тип.

Если изменить список на:

List<object> currentList = new List<object>(); 

он будет работать. Но могу ли я сделать это с помощью generics (T) вместо типа объекта?

+3

Нет, вы не можете ожидать, что ваш компилятор выведет тип, который вы предоставляете только во время выполнения. Это то, что вы делаете, используя отражение или что угодно, всегда будет возвращать «объект». Вы должны знать в компиляторе, какой именно тип он на самом деле. – HimBromBeere

+0

Вы пытались изменить свой метод на readObjects ()? и настроить ваш метод, чтобы получить тип на основе T ... – Rex

+0

Как вы называете 'ReadObjects'? И что вы делаете с 'currentList' впоследствии? – HimBromBeere

ответ

0

Вы можете создать Ис тип списка, который с помощью Activator, затем отливал в IList и использовать его:

private IList readObjects(System.Type objType) 
{ 
    var listType = typeof(List<>).MakeGenericType(curType); 
    var list = (IList)Activator.CreateInstance(listType); 

    // ... 

    while (_rs.Read()) 
    { 
     // ... 
     list.Add(_objItem); 
    } 
} 

list будет экземпляром List<YorActualType>.

Update

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

Поскольку вы предоставляете информацию о типе во время выполнения (curType может содержать информацию любого типа), компилятор не знает, какой именно тип будет использоваться, и вы не можете объявить свой метод для возврата чего-то конкретного. Разрешены только абстракции.

Давайте мне показать его слегка безумную, но демонстративный пример:

var types = new [] { typeof(int), typeof(string) }; 
var rand = new Random(); 
var list = readObjects(types[rand.Next(0,2)]; 

До самого последнего момента, даже вы не будете знать, что будет создан именно тип списка. Компилятор тоже не знает. Компилятор никогда не узнает, какой именно тип следует использовать, если вы не предоставите ему свои типы. Когда вы используете Type, он сообщает компилятору, что в этот метод будет передан некоторый регулярный параметр с типом Type. Нет данных для вывода типа во время компиляции. Эти данные могут быть переданы только через generic type parameters.

Итак, есть несколько способов, вы можете следовать:

  1. Обеспечить точные типы, нужно во время компиляции

    private List<T> readObjects<T>() 
    { 
        var objType = typeof(T); 
        var list = new List<T>(); 
        // rest of code.... 
    } 
    
  2. Использование отражения и базовые типы

    private IList readObjects(Type objType) 
    { 
        // rest of code with Activator and so on 
    } 
    

    И последующее использование зависит от ваших потребностей. Если вы знаете, какой тип вы собираетесь использовать, просто конвертировать:

    var list = (IList<MyType>)readObjects(typeof(myType)); 
    

    Но я думаю, в этом случае лучше в использовании способ # 1 с родовым аргументом.

    В противном случае вы собираетесь использовать отражение.Или некоторые базовые классы, интерфейсы и так далее. Это зависит от того, какую именно задачу вы собираетесь решить.

P.S. Вы можете узнать больше о generic types на MSDN.

+0

Однако это не помешает кому-либо помещать в этот список экземпляр любого другого типа, по крайней мере, во время компиляции - что невозможно. Вместо этого вы получаете «ArgumentException», указывающий, что предоставленный тип не может быть добавлен в общий список. – HimBromBeere

+0

@HimBromBe уверен, не будет никаких проверок времени компиляции. Но во время выполнения это вызовет исключение. Также его mothod для получения данных является общим, поэтому маловероятно, что ошибка будет выброшена. Я считаю, что метод возвращает значение типа общего аргумента. – lorond

+0

Спасибо. Это то, что я хотел. И если у меня есть метод, который возвращает этот список, как его объявить и называть? private List readObjects (System.Type objType) – Simon