10

I need to create at runtime instances of a class that uses generics, like class<T>, without knowing previously the type T they will have, I would like to do something like that:

public Dictionary<Type, object> GenerateLists(List<Type> types)
{
    Dictionary<Type, object> lists = new Dictionary<Type, object>();

    foreach (Type type in types)
    {
        lists.Add(type, new List<type>()); /* this new List<type>() doesn't work */
    }

    return lists;
}

...but I can't. I think it is not possible to write in C# inside the generic brackets a type variable. Is there another way to do it?

Jeff Atwood
  • 63,320
  • 48
  • 150
  • 153
Victor Rodrigues
  • 11,353
  • 23
  • 75
  • 107

2 Answers2

18

You can't do it like that - the point of generics is mostly compile-time type-safety - but you can do it with reflection:

public Dictionary<Type, object> GenerateLists(List<Type> types)
{
    Dictionary<Type, object> lists = new Dictionary<Type, object>();

    foreach (Type type in types)
    {
        Type genericList = typeof(List<>).MakeGenericType(type);
        lists.Add(type, Activator.CreateInstance(genericList));
    }

    return lists;
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • thanks again Jon. Does CreateInstance require an empty constructor? I don't have it, actually I need to input a parameter into it. – Victor Rodrigues Oct 24 '08 at 15:35
  • It does require a constructor. If you don't want to use a constructor you can use FormatterServices.GetUninitializedObject() in the System.Runtime.Serialization assembly. – Jason Jackson Oct 24 '08 at 15:38
  • 1
    @Victor: There are overloads of Activator.CreateInstance which take parameters, or you could use Type.GetConstructor and then invoke that. Basically there are plenty of ways of going from a type to an instance of a type - pick one :) – Jon Skeet Oct 24 '08 at 16:10
  • 1
    Note that this code isn't creating any instances of type though - just List, which doesn't care about what constructors are available for the type. – Jon Skeet Oct 24 '08 at 16:11
4

Depending on how often you're calling this method then using Activator.CreateInstance could be slow. Another option is to do something like this:

private Dictionary> delegates = new Dictionary>();

    public Dictionary<Type, object> GenerateLists(List<Type> types)
    {
        Dictionary<Type, object> lists = new Dictionary<Type, object>();

        foreach (Type type in types)
        {
            if (!delegates.ContainsKey(type))
                delegates.Add(type, CreateListDelegate(type));
            lists.Add(type, delegates[type]());
        }

        return lists;
    }

    private Func<object> CreateListDelegate(Type type)
    {
        MethodInfo createListMethod = GetType().GetMethod("CreateList");
        MethodInfo genericCreateListMethod = createListMethod.MakeGenericMethod(type);
        return Delegate.CreateDelegate(typeof(Func<object>), this, genericCreateListMethod) as Func<object>;
    }

    public object CreateList<T>()
    {
        return new List<T>();
    }

On the first hit it'll create a delegate to the generic method that creates the list and then puts that in a dictionary. On each subsequent hit you'll just call the delegate for that type.

Hope this helps!

jonnii
  • 28,019
  • 8
  • 80
  • 108