2009-07-20 22 views
96

En C# Tengo el siguiente objeto:¿Cómo crear dinámicamente un objeto C# genérico utilizando la reflexión?

public class Item 
{ } 

public class Task<T> 
{ } 

public class TaskA<T> : Task<T> 
{ } 

public class TaskB<T> : Task<T> 
{ } 

quiero crear dinámicamente taska o TaskB usando C# reflexión (Activator.CreateInstance). Sin embargo, no sabría el tipo de antemano, así que necesito crear dinámicamente TaskA basado en una cadena como "namespace.TaskA" o "namespace.TaskAB".

Respuesta

182

Echa un vistazo a este article y este simple example. traducción rápida de la misma a sus clases ...

var d1 = typeof(Task<>); 
Type[] typeArgs = { typeof(Item) }; 
var makeme = d1.MakeGenericType(typeArgs); 
object o = Activator.CreateInstance(makeme); 

por tu edición: Para ese caso, usted puede hacer esto ...

var d1 = Type.GetType("GenericTest.TaskA`1"); // GenericTest was my namespace, add yours 
Type[] typeArgs = { typeof(Item) }; 
var makeme = d1.MakeGenericType(typeArgs); 
object o = Activator.CreateInstance(makeme); 

para ver donde me ocurrió con backtick1 para el nombre de la clase genérica, vea this article.

Nota: si su clase genérica acepta varios tipos, debe incluir las comas cuando se omite los nombres de tipos, por ejemplo:

Type type = typeof(IReadOnlyDictionary<,>); 
+0

¿Se requiere el backtick, es decir, si se omite, el compilador supone que es 1? – richard

+7

Gracias por vincular el artículo de mi blog "Uso de Reflection para crear una instancia de una clase genérica en C# .Net" (http://omegacoder.com/?p=38) como el "Ejemplo simple". :-) Me alegra que el artículo encuentre una nueva vida. – OmegaMan

+0

¿cómo puedo comparar el tipo genérico usando la palabra clave 'is'? –

2

Me parece la última línea de su código de ejemplo debería ser simplemente :

Task<Item> itsMe = o as Task<Item>; 

¿O me está faltando algo?

+3

No te estás perdiendo nada. Soy yo quien no estaba pensando correctamente. ¡No debería haber bebido tanto alcohol la última noche! – Jeff

6

De hecho, no podría escribir la última línea.

Pero probablemente no desee crear el objeto, solo por el bien o crearlo. Probablemente desee llamar a algún método en su instancia recién creada.

A continuación, tendrá algo así como una interfaz:

public interface ITask 
{ 
    void Process(object o); 
} 

public class Task<T> : ITask 
{ 
    void ITask.Process(object o) 
    { 
     if(o is T) // Just to be sure, and maybe throw an exception 
     Process(o as T); 
    } 

    public void Process(T o) { } 
} 

y llama con:

Type d1 = Type.GetType("TaskA"); //or "TaskB" 
Type[] typeArgs = { typeof(Item) }; 
Type makeme = d1.MakeGenericType(typeArgs); 
ITask task = Activator.CreateInstance(makeme) as ITask; 

// This can be Item, or any type derived from Item 
task.Process(new Item()); 

En cualquier caso, no se le estáticamente convertirse a un tipo que don' saber de antemano ("makeme" en este caso). ITask te permite llegar a tu tipo de objetivo.

Si esto no es lo que quieres, probablemente necesitarás ser un poco más específico en lo que estás tratando de lograr con esto.

+0

Bueno, la pregunta ha cambiado :) –

+0

Tengo algo así como Proceso() en la Tarea . Y en mi caso, en realidad ya no me importa la última línea, ya que afirmas que simplemente estoy llamando a task.Process(), por lo tanto, si es capaz de codificar la última línea se vuelve irreverente. – Jeff

1

Asegúrese de estar haciendo esto por una buena razón, una función simple como la siguiente permitiría escribir estáticamente y le permite a su IDE hacer cosas como "Buscar referencias" y Refactorizar -> Renombrar.

public Task <T> factory (String name) 
{ 
    Task <T> result; 

    if (name.CompareTo ("A") == 0) 
    { 
    result = new TaskA(); 
    } 
    else if (name.CompareTo ("B") == 0) 
    { 
    result = new TaskB(); 
    } 

    return result; 
} 
+0

¿Por qué está utilizando .CompareTo? ¿Por qué no == o .Equals (si quieres más control). Tal vez un cambio sería incluso mejor. – Zyphrax

+0

Nunca he comprobado si C# hace una comparación de cadenas en == en lugar de una comparación de referencia. CompareTo y Equals deben tener la misma eficiencia de tiempo de ejecución si se implementaron correctamente. Un bloque de interruptores no tendría las mismas aceleraciones que poner un entero en un bloque de interruptores; se compilaría en un bloque if-else. – clemahieu

1

Sé que esta pregunta está resuelta pero, para el beneficio de cualquier otra persona que la lea; si usted tiene todos los tipos involucrados como cadenas, se puede hacer esto como un trazador de líneas:

IYourInterface o = (Activator.CreateInstance(Type.GetType("Namespace.TaskA`1[OtherNamespace.TypeParam]") as IYourInterface); 

Cada vez que he hecho este tipo de cosas, he tenido una interfaz que quería utilizar el código siguiente , así que fundí la instancia creada en una interfaz.

Cuestiones relacionadas