2009-10-13 6 views
1

IntroRécupérer une liste d'objet qui implémente une interface donnée

Je construis une architecture de plug-in dans mon application. Les plug-ins de mettre en œuvre une interface donnée IBasePlugin, ou une autre interface qui a hérité de l'interface de base:

interface IBasePlugin 
interface IMainFormEvents : IBasePlugin 

L'hôte est de chargement des assemblages de plug-ins, et crée ensuite l'objet approprié d'une classe d'application de l'IBasePlugin Interface ..

Ceci est la charge de la classe des plug-ins et instancier les objets:

public class PluginCore 
{ 
    #region implement singletone instance of class 
    private static PluginCore instance; 
    public static PluginCore PluginCoreSingleton 
    { 
     get 
     { 
      if (instance == null) 
      { 
       instance = new PluginCore(); 
      } 
      return instance; 
     } 
    } 
    #endregion 

    private List<Assembly> _PlugInAssemblies = null; 
    /// <summary> 
    /// Gets the plug in assemblies. 
    /// </summary> 
    /// <value>The plug in assemblies.</value> 
    public List<Assembly> PlugInAssemblies 
    { 
     get 
     { 
      if (_PlugInAssemblies != null) return _PlugInAssemblies; 

      // Load Plug-In Assemblies 
      DirectoryInfo dInfo = new DirectoryInfo(
       Path.Combine(
        Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), 
        "Plugins" 
        ) 
       ); 
      FileInfo[] files = dInfo.GetFiles("*.dll"); 
      _PlugInAssemblies = new List<Assembly>(); 
      if (null != files) 
      { 
       foreach (FileInfo file in files) 
       { 
        _PlugInAssemblies.Add(Assembly.LoadFile(file.FullName)); 
       } 
      } 

      return _PlugInAssemblies; 
     } 
    } 

    List<IBasePlugin> _pluginsList = null; 
    /// <summary> 
    /// Gets the plug ins instances. 
    /// all the plugins are being instanciated ONCE when this if called for the first time 
    /// every other call will return the existing classes. 
    /// </summary> 
    /// <value>The plug ins instances.</value> 
    public List<IBasePlugin> PlugInInstances 
    { 
     get 
     { 
      if (_pluginsList != null) return _pluginsList; 

      List<Type> availableTypes = new List<Type>(); 

      foreach (Assembly currentAssembly in this.PlugInAssemblies) 
       availableTypes.AddRange(currentAssembly.GetTypes()); 

      // get a list of objects that implement the IBasePlugin 
      List<Type> pluginsList = availableTypes.FindAll(delegate(Type t) 
      { 
       List<Type> interfaceTypes = new List<Type>(t.GetInterfaces()); 
       return interfaceTypes.Contains(typeof(IBasePlugin)); 
      }); 

      // convert the list of Objects to an instantiated list of IBasePlugin 
      _pluginsList = pluginsList.ConvertAll<IBasePlugin>(delegate(Type t) { return Activator.CreateInstance(t) as IBasePlugin; }); 

      return _pluginsList; 
     } 
    } 

La question

Actuellement, un module qui prend en charge les plug-ins, utilise les PlugInInstances propriété pour récupérer les IBasePlugins liste. Ensuite, il itère les objets qui demandent qui implémente une interface enfant donnée.

foreach (IBasePlugin plugin in PluginCore.PluginCoreSingleton.PlugInInstances) 
{ 
    if (plugin is IMainFormEvents) 
    { 
     // Do something 
    } 
} 

Je voudrais améliorer cette technique en ayant une fonction qui reçoit une interface enfant donné, et retourner une liste de ces interfaces. Le hic, c'est qu'aucun casting ne devrait être fait dans l'appelant.

code pseudo:

void GetListByInterface(Type InterfaceType, out List<InterfaceType> Plugins) 

Avez-vous une recommandation sur la façon de mettre en œuvre ce?

+4

Avez-vous envisagé d'utiliser Microsoft Managed Extensibility Framework à la place? http://www.codeplex.com/MEF – TrueWill

+0

@TrueWill: Je vais y regarder – Amirshk

+0

@TrueWill: J'ai un code qui fait exactement cela à la maison. Je vais jeter un oeil dans quelques heures. –

Répondre

2

Vous pouvez essayer quelque chose comme ceci:

void GetListByInterface<TInterface>(out IList<TInterface> plugins) where TInterface : IBasePlugin 
{ 
    plugins = (from p in _allPlugins where p is TInterface select (TInterface)p).ToList(); 
} 
+0

@Andrew: merci, juste ce que je cherchais. Bien que je devais l'implémenter sans LINQ, puisque j'ai la conformité .NET 2. – Amirshk

+0

J'aurais dû dire _allPlugins.OfType () mais ce n'est pas vraiment important car vous n'utilisez pas linq. –

2

J'ai utilisé une approche similaire pour mon système de tournoi.

Vous pouvez jeter un oeil à la source ici: http://tournaments.codeplex.com/SourceControl/ListDownloadableCommits.aspx

Dans le coffre/TournamentApi/Plugins/PluginLoader.cs, j'ai défini les méthodes nécessaires pour charger les plug-ins d'un assemblage arbitraire.


L'idée que j'utilisé était celui d'une classe plugin-usine-recenseur qui se trouve, instancié et appelé à produire des instances d'usine plugin.

est ici la viande du code:

List<IPluginFactory> factories = new List<IPluginFactory>(); 

try 
{ 
    foreach (Type type in assembly.GetTypes()) 
    { 
     IPluginEnumerator instance = null; 

     if (type.GetInterface("IPluginEnumerator") != null) 
     { 
      instance = (IPluginEnumerator)Activator.CreateInstance(type); 
     } 

     if (instance != null) 
     { 
      factories.AddRange(instance.EnumerateFactories()); 
     } 
    } 
} 
catch (SecurityException ex) 
{ 
    throw new LoadPluginsFailureException("Loading of plugins failed. Check the inner exception for more details.", ex); 
} 
catch (ReflectionTypeLoadException ex) 
{ 
    throw new LoadPluginsFailureException("Loading of plugins failed. Check the inner exception for more details.", ex); 
} 

return factories.AsReadOnly(); 
0

Je l'aide d'un conteneur du CIO pour effectuer la recherche plug-in. MEF peut être un peu plus, mais StructureMap est une DLL unique, et a un support intégré pour ce hors de la boîte.

Vous pouvez analyser un dossier à assembler contenant des objets qui implémentent des interfaces et les charger dans votre application facilement. StructureMap on SourceForge

Exemple de balayage dans la méthode de ObjectFactory Configurer:

 Scan(scanner => 
     { 
      string assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 

      scanner.AssembliesFromPath(assemblyPath, assembly => { return assembly.GetName().Name.StartsWith("Plugin."); }); 

      scanner.With(typeScanner); 
     }); 

Le scanner de type ITypeScanner implémente et peut examiner les types sont vérifier si le type est assignable au type d'interface en question. Il y a de bons exemples dans le lien de documentation joint.

Questions connexes