2008-10-24 22 views
9

Es necesario crear instancias en tiempo de ejecución de una clase que utiliza los genéricos, como class<T>, sin conocer previamente el tipo T tendrán, me gustaría hacer algo así:¿Es imposible usar Generics de forma dinámica?

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

... pero hipocresía. Creo que no es posible escribir en C# dentro de los corchetes genéricos una variable de tipo. ¿Hay alguna otra forma de hacerlo?

+0

¿Puede dar un motivo por el que desea crear un objeto genérico seguro en tiempo de ejecución? –

+0

Lo he hecho varias veces, especialmente al transferir memorias intermedias de protocolo. –

+0

Estoy haciendo un adaptador a un marco de persistencia aquí en mi trabajo, y las clases necesitan esta información para realizar su trabajo. –

Respuesta

17

No se puede hacer de esa manera - el punto de genéricos es sobre todo tiempo de compilación seguridad de tipos - pero puede hacerlo con la reflexión:

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; 
} 
+0

gracias otra vez Jon. ¿CreateInstance requiere un constructor vacío? No lo tengo, de hecho necesito ingresar un parámetro en él. –

+0

Requiere un constructor. Si no desea utilizar un constructor, puede usar FormatterServices.GetUninitializedObject() en el ensamblado System.Runtime.Serialization. –

+1

@Victor: hay sobrecargas de Activator.CreateInstance que toman parámetros, o puede usar Type.GetConstructor y luego invocar eso. Básicamente hay muchas maneras de pasar de un tipo a una instancia de un tipo: elija uno :) –

4

Dependiendo de la frecuencia con la que estás llamando a este método y luego usando Activator.CreateInstance podría ser lento. Otra opción es hacer algo como esto:

private Dictionary> deletes = 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>(); 
    } 

En el primer golpe creará un delegado al método genérico que crea la lista y luego la coloca en un diccionario. En cada golpe posterior, simplemente llamará al delegado para ese tipo.

Espero que esto ayude!

+0

Gracias jonni, llamo a este método solo una vez, de lo contrario, mantener a un delegado sería una buena opción. –

Cuestiones relacionadas