动态加载与插件系统的初步实现(一):反射与MEF解决方案

涉及内容:

  • 反射与MEF解决方案
  • AppDomain卸载与代理
  • WinForm、WcfRestService示

PRRT1: 反射实现

插件系统的基本目的是实现宿主与组件的隔离,核心是作为接驳约定的接口,宿主使用类型发现及挂载插件,以下是反射实现。

创建类库项目Plugin,添加接口IPlugin:

public interface IPlugin
{
    String DoStuff();
}

创建控制台程序HostApp,添加对Plugin项目的引用,Main方法代码:

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<Type> pluginTypes = GetPluginTypes();

        foreach (Type pluginType in pluginTypes)
        {
            IPlugin plugin = (IPlugin)Activator.CreateInstance(pluginType);
            Console.WriteLine(plugin.DoStuff());
        }
    }

    private static IEnumerable<Type> GetPluginTypes()
    {
        String root = AppDomain.CurrentDomain.BaseDirectory;
        String[] files = Directory.GetFiles(root, "*.dll", SearchOption.TopDirectoryOnly);

        foreach (String file in files)
        {
            Type[] types = Assembly.LoadFrom(file).GetTypes();
            foreach (Type type in types)
            {
                if (type.IsClass && typeof(IPlugin).IsAssignableFrom(type))
                {
                    yield return type;
                }
            }
        }
    }
}

创建类库项目MyPlugin1,添加对Plugin项目的引用,添加Plugin1类并实现IPlugin:

public class Plugin1: IPlugin
{
    public String DoStuff()
    {
        return "MyPlugin1 Plugin1.DoStuff";
    }
}

修改该项目的属性,在“生成”选项卡中找到输出,将“输出路径”指向HostApp下的bin\Debug文件夹,运行。

宿主使用无参的IPlugin子类完成组件调用。代码逻辑并不复杂但我们还有更优雅的解决方式即MEF框架,这里拿MEF的完成所需功能,组件生命周期等内容并不深入讨论,如有需求请自行MSDN。

 

PRRT2: MEF实现

MEF框架以Import、Export特性为功能入口,修改MyPlugin项目,引用System.ComponentModel.Composition,为MyPlugin添加Export特性:

[Export(typeof(IPlugin))]
public class Plugin1: IPlugin
{
    public String DoStuff()
    {
        return "MyPlugin1 Plugin1.DoStuff";
    }
}

注意Export明确指定导出类型为IPlugin,在Plugin项目中添加类PluginProvider,引用System.ComponentModel.Composition和System.ComponentModel.Composition.Hosting,添加IEnumerable<Lazy<IPlugin>>类型只读属性并标注ImportMany特性:

public class PluginProvider
{
    [ImportMany]
    public IEnumerable<Lazy<IPlugin>> Plugins { get; private set; }

    public PluginProvider()
    {
        AggregateCatalog catalog = new AggregateCatalog();
        catalog.Catalogs.Add(new DirectoryCatalog("."));
        CompositionContainer container = new CompositionContainer(catalog);
        container.ComposeParts(this);
    }
}

抽象基类ComposablePartCatalog表示组件目录,子类DirectoryCatalog使用指定目录进行搜索。PluginProvider使用了当前程序运行目录作为dll路径。同时导入点所在字段或属性可以是IEnumerable<T>、IEnumerable<Lazy<T>>、IEnumerable<Lazy<T, TMetadata>>等,延迟绑定能相对降低内存开销,这里使用了第2种,接着修改Main方法:

class Program
{
    static void Main(string[] args)
    {
        PluginProvider pluginProvider = new PluginProvider();
        foreach (Lazy<IPlugin> plugin in pluginProvider.Plugins)
        {
            Console.WriteLine(plugin.Value.DoStuff());
        }
    }
}

运行得到同样的结果,代码更加优雅;根据需求,修改PluginProvider的导入逻辑及使用泛型版本,将得到更多的灵活性。代码文件

附求职信息:目前在北京,寻求.Net相关职位,偏向后端,请邮件jusfr.v#gmail.com,替换#为@,沟通后奉上简历。

posted @ 2013-06-22 16:08  Jusfr  阅读(2575)  评论(1编辑  收藏  举报