2011-01-28 4 views
7

Ok, j'ai mis cette idée toute la journée et j'ai atteint la partie où j'avoue que je ne sais pas. Il est possible que ce que je fais soit juste stupide et qu'il y ait un meilleur moyen, mais c'est là que ma réflexion m'a amené.Comment pouvez-vous convertir un type en utilisant le nom du type en tant que chaîne?

Je tente d'utiliser une méthode générique pour charger des formulaires dans WinForms:

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

Les formes sont chargées par un ToolStripMenuItem (soit par la sélection de l'élément ou en utilisant l'option Ouvrir le menu Windows). Ils sont paresseux, donc il y a des champs pour les formulaires dans le parent MDI, mais ils sont nuls jusqu'à ce qu'ils soient nécessaires. J'ai une méthode commune utilisée pour ToolStripMenuItem_Click qui gère tous les clics de l'élément de menu. La méthode n'a aucun moyen de savoir quel formulaire est appelé, sauf que le nom du ToolStripMenuItem correspond à un modèle choisi pour les noms de classe de formulaire auxquels ils correspondent. Ainsi, en utilisant le nom du ToolStripMenuItem, je peux deviner le nom du type de formulaire demandé et le nom du champ privé alloué pour stocker la référence pour ce formulaire. En utilisant cela, je peux soit utiliser une instruction switch croissante/contractante avec des types codés en dur et des chaînes de caractères pour appeler la méthode avec le type spécifique (indésirable), soit utiliser Reflection pour obtenir le champ et créer l'instance du type. Le problème pour moi est, System.Activator.CreateInstance fournit un ObjectHandler qui ne peut pas être moulé aux types dont j'ai besoin. Voici un extrait de ce que j'ai jusqu'à présent:

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

Je connais le nom de chaîne du type qui va dans (????) mais à la compilation, je ne sais pas le type parce qu'il change . J'ai essayé un tas de façons de faire fonctionner cette distribution/instanciation, mais aucune n'a réussi. Je voudrais vraiment savoir s'il est possible d'effectuer une telle distribution en connaissant le type seulement comme une chaîne. J'ai essayé d'utiliser Type.GetType(string, string) pour effectuer le cast, mais le compilateur n'a pas aimé. Si quelqu'un a une idée différente sur la façon de charger les formulaires dynamiquement parce que je le fais simplement bêtement, s'il vous plaît laissez-moi savoir à ce sujet.

+0

créer un objet FormToLoad et le transformer en FormWithWorker aux points LoadForm et SetValue? – asawyer

Répondre

5

Vous feriez mieux avec le other overload qui prend un Type et en utilisant par exemple Type.GetType(string).

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

Je me souviens qu'à un moment donné, ce post mentionnait spécifiquement l'utilisation de la méthode '.UnWrap()' sur System.Activator.CreateInstance'. Il ne le dit pas maintenant, mais il devrait parce que la solution à mon problème était de déclarer le champ privé avec le type de la classe de base, puis appelez CreateInstance ("namespace", "type"). Unwrap() '. Cela a créé le type approprié et a permis à la méthode d'instancier correctement. –

12

Ce problème est généralement résolu par la conversion d'une classe de base commune ou d'une interface de tous les types de potentiel.

En C# 4, vous pouvez également l'affecter à une variable dynamic pour contenir la valeur de retour et appeler des méthodes arbitraires. Les méthodes seront liées tardivement. Cependant, je préfère m'en tenir à la première solution autant que possible.

+0

Vote en cours. J'ai fait quelque chose de très similaire en utilisant une interface et ça marche bien. – Chuck

+0

J'ai essayé cette méthode, mais le problème que j'ai rencontré était que le type dynamique assigné était la classe de base et non la classe réelle, donc en appelant la méthode SetValue, elle a jeté une exception de type casting. –

+0

@Joel comme le souligne l'autre réponse, vous devez utiliser la surcharge qui renvoie l'objet lui-même. Apparemment, la surcharge que vous utilisez est utile pour activer les objets remoting .NET. –

2

Selon ce que vous avez, FormWithWorker doit être (au moins) en tant que classe de base du type que vous instanciation, vous pouvez le faire:

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

J'ai essayé cela, mais j'ai reçu une erreur de conversion en raison de la méthode 'System.Activator.CreateInstance' renvoyant un ObjectHandler. Je viens de lire l'idée de @Andras Vass d'appeler 'Unwrap()' et de combiner les 2 est ce que je vais essayer ensuite. –

0

Même si une interface commune est une façon d'aborder cette problème, les interfaces ne sont pas pratiques pour tous les scenerioes. La décision ci-dessus est celle d'aller avec un modèle d'usine (déclaration de commutateur - sélection de classe concrète) ou utiliser la réflexion. Il y a un post de pile qui s'attaque à ce problème. Je crois que vous pouvez directement appliquer à votre question:

Method Factory - case vs. reflection

Questions connexes