Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create list of generic object at runtime

Tags:

c#

generics

I have object of some type known at runtime and I read and deserialize this object from database. It works. Now I would like to add it to some list:

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

It won't work. The error is:

curType is a variable but is used like a type.

If I change list to:

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

it will work. But can i do this with generics(T) instead of object type?

like image 492
Simon Avatar asked Oct 18 '22 21:10

Simon


1 Answers

You can easly create type of list you want via Activator, then cast to IList and use it:

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 will be instance of List<YorActualType>.

Update

When you declaring your method with generic arguments, it assumes you provide type info during compile time. Otherwise you need to use reflection.

Since you providing type info in run time (curType can hold any type info), compiler does not know what exactly type will be used, and you cannot declare your method to return something concrete. Only abstractions allowed.

Let's me show it on slightly insane but demonstrative example:

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

Until the very last moment even you will not know what exactly type of list will be created. Compiler does not know too. Compiler will never know what exactly type should be used if you not provide him with you types. When you use Type it only tells compiler that some regular parameter with type Type will be passed into the method in run time. There is no data to infer a type during compile time. That data can be passed only via generic type parameters.

So, there is several ways you can follow:

  1. Provide exact types you need at compile time

    private List<T> readObjects<T>()
    {
        var objType = typeof(T);
        var list = new List<T>();
        // rest of code....
    }
    
  2. Use reflection and base types

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

    And later usage depends on your needs. If you know what type you going to use, simply convert:

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

    But I guess in that case better use way #1 with generic argument.

    Otherwise you going to use reflection. Or some base classes, interfaces and so on. It depends on what exactly task you going to solve.

P.S. You can read more about generic types on MSDN.

like image 100
lorond Avatar answered Oct 20 '22 23:10

lorond