2011-01-28 31 views
7

Ok, he aprovechado esta idea todo el día, y he llegado a la parte en la que admito que simplemente no sé. Es posible que lo que estoy haciendo sea estúpido y haya una mejor manera, pero aquí es donde mi pensamiento me ha traído.¿Cómo se puede convertir a un tipo usando el nombre del tipo como una cadena?

Estoy intentando utilizar un método genérico para cargar formas en WinForms:

protected void LoadForm<T>(ref T formToShow, bool autoLoaded) where T : FormWithWorker, new() 
{ 
    // Do some stuff 
} 

Las formas son cargados por un ToolStripMenuItem (ya sea a través de la selección del elemento o utilizando el elemento de menú Abrir Windows). Son de carga lenta, por lo que hay campos para los formularios dentro del padre MDI, pero son nulos hasta que se necesiten. Tengo un método común utilizado para ToolStripMenuItem_Click que maneja todos los clics de elemento de menú. El método no tiene una forma real de saber qué formulario se solicita, excepto que el nombre del ToolStripMenuItem coincide con un patrón elegido para los nombres de clase de formulario a los que corresponden. Entonces, usando el nombre del ToolStripMenuItem, puedo deducir el nombre del tipo de formulario que se solicita y el nombre del campo privado asignado para almacenar la referencia para ese formulario.

Usando eso, puedo usar una instrucción de cambio creciente/contratante con tipos de código fijo y cadena de coincidencias para llamar al método con el tipo específico establecido (indeseable), o puedo usar Reflection para obtener el campo y crear la instancia del tipo El problema para mí es que System.Activator.CreateInstance proporciona un ObjectHandler que no se puede convertir a los tipos que necesito. Aquí hay un fragmento de lo que tengo hasta ahora:

string formName = "_form" + ((ToolStripMenuItem)sender).Name.Replace("ToolStripMenuItem", ""); 
string formType = formName.Substring(1); 

FieldInfo fi = this.GetType().GetField(formName, BindingFlags.NonPublic | BindingFlags.Instance); 

FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this); 
if (formToLoad == null) 
{ 
    formToLoad = (????)System.Activator.CreateInstance("MyAssemblyName", formType); 
} 

this.LoadForm(ref formToLoad, false); 
fi.SetValue(this, formToLoad); 

sé que el nombre de la cadena del tipo que va en los (????), pero en tiempo de compilación no sé el tipo, ya que cambia . He intentado varias formas de hacer funcionar este molde/instanciación, pero ninguno ha tenido éxito. Me gustaría mucho saber si es posible realizar dicho reparto sabiendo el tipo solo como una cadena. Intenté usar Type.GetType(string, string) para realizar el reparto, pero al compilador no le gustó. Si alguien tiene una idea diferente sobre cómo cargar los formularios dinámicamente porque lo estoy haciendo de forma estúpida, házmelo saber.

+0

hacer el objeto formToLoad y convertirlo a FormWithWorker en los puntos LoadForm y SetValue? – asawyer

Respuesta

5

Usted sería mejor con la other overload que toma un Type y el uso de, por ejemplo, Type.GetType(string).

FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this); 
if (formToLoad == null) 
{ 
    formToLoad = 
     (FormWithWorker)System.Activator.CreateInstance(Type.GetType("MyNamespace.MyFormType")); 
} 
+0

Recuerdo que en un momento esta publicación menciona específicamente el uso del método '.UnWrap()' en 'System.Activator.CreateInstance'. No lo dice ahora, pero debería hacerlo porque la solución a mi problema era declarar el campo privado con el tipo de clase base y luego llamar a 'CreateInstance (" namespace "," type "). Unwrap()'. Esto creó el tipo apropiado y permitió que el método creara una instancia adecuada. –

12

Este problema generalmente se resuelve mediante conversión a una clase base o interfaz común de todos los tipos posibles.

En C# 4, también puede asignarlo a una variable dynamic para mantener el valor de retorno y llamar a métodos arbitrarios en él. Los métodos serán atados tarde. Sin embargo, prefiero mantener la solución anterior siempre que sea posible.

+0

Hasta la votación. He hecho algo muy similar usando una interfaz y funciona muy bien. – Chuck

+0

Probé este método, pero el problema que encontré fue que el tipo dinámico que terminaba asignando era la clase base y no la clase real, por lo que al llamar al método SetValue, lanzaba una excepción de conversión de tipos. –

+0

@Joel como señala la otra respuesta, debe usar la sobrecarga que devuelve el objeto en sí. Aparentemente, la sobrecarga que está utilizando es útil para activar objetos remotos .NET. –

2

De acuerdo con lo que tienes, FormWithWorker debe haber (al menos) como la clase base del tipo que está instanciar, por lo que se puede hacer esto:

FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this); 
if (formToLoad == null) 
{ 
    formToLoad = (FormWithWorker)System.Activator.CreateInstance("MyAssemblyName", formType); 
} 
+0

Intenté esto, pero recibí un error de conversión debido al método 'System.Activator.CreateInstance' que devuelve un ObjectHandler. Acabo de leer la idea de @Andras Vass de llamar a 'Unwrap()' y combinar el 2 es lo que intentaré a continuación. –

0

Si bien una interfaz común es una manera de acercarse a este problema, las interfaces no son prácticas para todos los escenarios. La decisión anterior es una de ir con un patrón de fábrica (cambiar instrucción - selección de clase concreta) o usar reflexión. Hay una publicación de pila que aborda este problema. Creo que se puede aplicar directamente a esta el problema:

Method Factory - case vs. reflection

Cuestiones relacionadas