2009-07-11 5 views
1

Visualisez la situation suivante. J'ai un document XML comme suit,Coulée dynamique en C#

<Form> 
    <Control Type="Text" Name="FirstName" /> 
    <Control Type="DateTime" Name="DateOfBirth" /> 
    <Control Type="Text" Name="PlaceOfBirth" /> 
</Form> 

J'ai une classe abstraite appelée contrôle avec une seule méthode abstraite appelée processus qui prend un seul paramètre de HttpRequest. J'ai également deux autres classes qui sont dérivées de contrôle appelé TextControl et DateTimeControl. Text et DateTime remplacent la méthode Process pour fournir leurs propres implémentations.

J'ai aussi une classe Form qui a une méthode Process prenant un seul paramètre de type HttpRequest, et un constructeur qui prend un seul paramètre de type XmlDocument.

Une nouvelle instance de Form est créée et le XML ci-dessus est transmis via le paramètre XmlDocument (la manière dont nous obtenons de la chaîne vers un XmlDocument n'est pas pertinente). J'appelle ensuite la méthode Process sur l'instance de la forme que je viens de créer et passe un paramètre de type HttpRequest comme attendu.

Tout va bien jusqu'ici. Maintenant sur la question.

Pour rendre le traitement des contrôles extensible, j'aimerais pouvoir mapper les classes aux types de contrôle.

par ex.

Form.RegisterControl("Text", Text) 
Form.RegisterControl("DateTime", DateTimeControl) 

Dans la méthode du processus de la forme que je voudrais itterate sur chaque nœud de contrôle dans le document (comment le faire est à nouveau hors de propos) et instancier une instance de la classe qui correspond à son type en fonction des classes enregistrées par notre méthode RegisterControl. Je serais capable à ce stade de spécifier qu'ils sont dérivés de Control mais ne peuvent pas spécifier explicitement leur type. Parce qu'ils sont tous deux dérivés de contrôle, je veux appeler la méthode de processus qui, je sais, sera mis en œuvre.

Est-ce encore possible? Si oui, comment pourrais-je y aller?

Répondre

4

(Cette réponse est à certains égards, deux réponses différentes, selon le sens de votre question. Si tout va bien une partie est utile, de toute façon :)


Probablement la meilleure façon est de passer dans un argument qui est une fonction qui peut être utilisée pour créer le nouveau contrôle au bon moment. Si vous utilisez C# 3, c'est aussi simple que:

Form.RegisterControl("Text",() => new Text()) 

Sinon, vous pouvez en faire une méthode générique avec deux contraintes: l'un pour être un contrôle et un pour avoir un constructeur sans paramètre.

public void RegisterControl<T>(string name) where T : Control, new() 

puis appelez avec:

Form.RegisterControl<Text>("Text"); 
Form.RegisterControl<DateTimeControl>("DateTime"); 

RegisterControl aurait de se rappeler typeof(T) quel que soit le stockage qu'il utilise, mais au moins il pourrait être raisonnablement sûr que Activator.CreateInstance(Type) fonctionnerait plus tard - et vous d avoir une vérification à la compilation. Personnellement, j'aime bien la flexibilité de la première forme - si vous passez dans un délégué, vous pouvez choisir d'utiliser un singleton, ou peut-être un constructeur interne ou même privé (selon l'endroit d'où il est appelé).Vous pouvez également utiliser un délégué qui a pris les données appropriées aussi:

Form.RegisterControl("Text", data => new Text(data)); 

Vous ne pouvez pas exprimer ce genre de constructeur avec une contrainte générique, et il serait relativement difficile d'invoquer plus tard de toute façon.


EDIT: Il est possible que Mehrdad et moi-même ayons mal interprété la question. Avez-vous des surcharges différentes de RegisterControl en fonction du type de contrôle? Si oui, alors les seules façons d'appeler directement la surcharge correcte au moment de l'exécution sont soit d'utiliser la réflexion ou d'utiliser le typage dynamique en C# 4.

Une autre alternative serait d'utiliser la double répartition - mettre une méthode dans le contrôle lui-même qui sait comment s'enregistrer avec un formulaire. Cela serait spécifié dans l'interface ou la classe de base, mais remplacé dans les sous-classes de béton. Donc, votre code qui est actuellement:

Form.RegisterControl("Text", control); 

deviendrait:

control.RegisterWith(Form, "Text"); 

Ce pourrait alors appeler la surcharge correcte sans problème. Fondamentalement, vous devez vous rappeler que la résolution de surcharge est effectuée au moment de la compilation, mais la résolution de substitution est effectuée au moment de l'exécution - donc si vous voulez faire quelque chose de dynamique, essayez de l'approcher avec le polymorphisme.

+0

Merci Jon! La première réponse devrait être correcte (vous avez supposé correctement). Je craque maintenant! –

+0

Merci Jon, cela a parfaitement fonctionné. –