Je me demande ce que la recommandation générale (attribut, interface, classe abstraite, ou une combinaison de ceux-ci) pour la mise en œuvre suivante:attribut, interface ou classe abstraite
/// <summary>
/// Loads class specific information into a list for serialization. The class must extend PlugIn.
/// The filenames parameter is passed from a FileDialog.
/// </summary>
/// <param name="filenames">Accepts any number of filenames with fully qualified paths.</param>
public static void ExtractPlugInData(params string[] filenames)
{
List<Type> l;
foreach (string f in filenames)
{
Assembly a = Assembly.LoadFrom(f);
// lambda expression selects any class within a library extending the abstract PlugIn class
l = a.GetTypes().Where(type => typeof(PlugIn).IsAssignableFrom(type)).ToList<Type>();
if (l.Count > 0)
// write data to serializable class
WritePlugInData(f , l);
else
// throw exception
WriteLine("{0} :: No PlugIn Data Found" , a.FullName);
}
}
Je me rends compte qu'il ya des avantages et inconvénients de chaque méthode. Évidemment, les attributs nécessitent une certaine réflexion (tout comme l'implémentation abstraite de l'extension et de l'interface). Une classe abstraite prend notre seul héritage de base, et tout changement futur dans une interface peut casser n'importe quel plugin existant. Donc, comme je le vois, ce sont les inconvénients.
Les performances ne sont pas un problème (à moins qu'il y ait quelque chose que je ne vois pas) puisque toute réflexion n'est effectuée qu'une fois lorsqu'une classe qualifiée est extraite. Les principaux éléments de données enregistrés sont le nom du plugin ("MyPlugIn"), l'espace de nom ("SuperPlugIn.PlugInClass") et le chemin de démarrage du fichier .dll. À l'heure actuelle, avec la classe PlugIn abstraite, l'extension des propriétés est appliquée. C'est plus ou moins le même résultat si on implémente une interface (IPlugIn).
Nous autorisons l'écriture de plugins personnalisés par les utilisateurs finaux. Avec les plugins que nous écrivons en interne, il est facile d'enseigner et de faire appliquer une structure requise pour notre application afin d'obtenir une classe qualifiée. Cependant, je considère également les difficultés ou les inconvénients pour l'utilisateur final en cas de changement majeur.
Tous les commentaires, suggestions et questions sont les bienvenus !!
Note: merci à Jon Skeet pour l'expression lambda dans l'extrait. :)
EDIT: J'aurais dû noter au début que cela devait être indépendant de la plate-forme (c'est-à-dire mono). MISE À JOUR: D'après les excellentes recommandations, commentaires et liens ci-dessous, une combinaison d'attributs et d'interfaces est la meilleure approche. Les attributs permettent de charger l'assemblage et de vérifier les informations et les implémentations requises de manière plutôt sûre sans avoir à instancier les classes/objets du plugin. Ceci est idéal dans les situations où des tiers ou des utilisateurs finaux sont autorisés à créer des plugins personnalisés. Nous pouvons vérifier que l'implémentation correcte du contrat est en place lorsque l'attribut indique qu'il est supposé l'être. Nous pouvons vérifier les dépendances et les ressources requises et alerter le développeur de tout problème avant que l'on instance quoi que ce soit.
Je suis sûr que ce ne sera pas une interface. – Jay
Beaucoup de références données par Aaronaught finissent par utiliser des interfaces. Assez étonnamment Mono.Addins (http://www.mono-project.com/Mono.Addins) va la route d'attribut. – IAbstract
C'est vrai, c'est pourquoi j'ai qualifié ma réponse à l'origine du mot "presque". ;) Il n'y a pas de réponse * right * ou * wrong * et vous pouvez même combiner les deux approches (utilisez les attributs pour trouver les classes tout en leur appliquant une interface plugin). – Aaronaught