检索实现给定接口的对象列表

时间:2009-10-13 22:22:58

标签: c# generics architecture plugins interface

简介

我正在我的应用中构建插件架构。插件实现了给定的接口 IBasePlugin ,或者从基接口继承的其他接口:

interface IBasePlugin
interface IMainFormEvents : IBasePlugin

主机正在加载插件程序集,然后创建实现 IBasePlugin 接口的任何类的相应对象。

这是加载插件并实例化对象的类:

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

问题

目前,任何支持插件的模块都使用 PlugInInstances 属性来检索 IBasePlugins 列表。然后它迭代查询谁正在实现给定子接口的对象。

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

我想通过一个接收给定子接口的函数来改进这种技术,并返回这些接口的列表。问题是不应该在调用者中进行任何转换。

伪代码:

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

您是否有关于如何实施此建议的建议?

3 个答案:

答案 0 :(得分:2)

您可以尝试这样的事情:

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

答案 1 :(得分:2)

我在锦标赛系统中采用了类似的方法。

您可以在这里查看来源: http://tournaments.codeplex.com/SourceControl/ListDownloadableCommits.aspx

在trunk / TournamentApi / Plugins / PluginLoader.cs中,我已经定义了加载任意程序集插件所需的方法。


我使用的想法是Plugin-Factory-Enumerator类,可以找到,实例化并调用它来生成插件工厂实例。

以下是代码的内容:

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

答案 2 :(得分:0)

我会使用IOC容器来执行插件查找。 MEF可能有点多,但StructureMap是一个单独的DLL,内置支持这个开箱即用。

您可以扫描包含实现接口的对象的程序集的文件夹,并轻松将其加载到应用程序中。 StructureMap on SourceForge

在ObjectFactory的Configure方法中扫描的示例:

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

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

            scanner.With(typeScanner);
        });

类型扫描程序实现ITypeScanner,并且可以检查类型是否检查类型是否可分配给相关的接口类型。附带的文档链接中有很好的例子。