MEF 插件式开发 - 小试牛刀

简介: 原文:MEF 插件式开发 - 小试牛刀目录 MEF 简介 实践出真知面向接口编程 控制反转(IOC) 构建入门级 MEF 相关参考 MEF 简介 Managed Extensibility Framework 即 MEF 是用于创建轻量、可扩展应用程序的库。
原文: MEF 插件式开发 - 小试牛刀

目录


MEF 简介

Managed Extensibility Framework 即 MEF 是用于创建轻量、可扩展应用程序的库。 它让应用程序开发人员得以发现和使用扩展且无需配置。 它还让扩展开发人员得以轻松地封装代码并避免脆弱的紧密依赖性。 MEF 让扩展不仅可在应用程序内重复使用,还可以跨程序重复使用。

在进行传统的 C/S 端开发,如果项目不是特别复杂,常规的开发模式还是可以应对的。但是一旦场景复杂度提升,一个小小业务功能的修改就需要更新整个客户端,这个对于开发者来说是不能忍受的。因此微软为我们引入了 MEF 的开发模式。允许我们将众多的业务模块拆分开来设计成独立的 DLL,然后由客户端来进行统一加载,这样就能解决上述我们所说的痛点。

实践出真知

创建一个高扩张的 MEF 框架涉及的技术点较多。为了方便初学者能较快理解,上手实践,我这里主要通过 3 个方面来进行相关叙述。

面向接口编程

如果你还不能理解什么是面向接口编程的话,那你应该还不能区分抽象类和接口之间的区别。其实在刚开始的时候我也不是很能理解,直到我看到了一句话:抽象类规定了你是什么,接口规定了你能干什么,只要你能理解这句话,那么你应该就明白什么是面向接口编程,这种编程方式的好处是统一化了业务的暴露方式,方便外部使用。下面我们看一个简单的例子。

public interface IMessage
{
    void Send();
}

public class EmailService : IMessage
{
    public void Send()
    {
        Console.WriteLine("Email Send Message");
    }
}

public class SMSService : IMessage
{
    public void Send()
    {
        Console.WriteLine("SMS Send Message");
    }
}

class Program
{
    static void Main(string[] args)
    {
        IMessage email = new EmailService();
        email.Send();

        IMessage sms = new SMSService();
        sms.Send();

        Console.ReadKey();
    }
}

上述代码中,我们创建了一个 IPlugin 的接口,接口定义了一个 ShowPluginName() 方法,然后我们再定义了两个独立的类来分别继承该接口并实现相应的接口函数。在主函数中,我们只需要定义一个接口类型的对象,然后接收一个具体的类型实例,函数就会输出对应的正确信息。这样编程的好处就不言而喻了。代码很简单,这里就不过多描述。输出结果如下图所示

img_85f214bcf897b0e4fbea70910efac44d.png

控制反转(IOC)

所谓控制反转,就是将对象初始化的控制权交出去。要实现控制反转,我们需要有面向接口编程的接口,同样的,这里也是展示一个代码段来叙述。

public interface IMessage
{
    void Send();
}

public class EmailService : IMessage
{
    public void Send()
    {
        Console.WriteLine("Email Send Message");
    }
}

public class SMSService : IMessage
{
    public void Send()
    {
        Console.WriteLine("SMS Send Message");
    }
}

public static class Factory
{
    public static EmailService GetEmailService() => new EmailService();

    public static SMSService GetSMSService() => new SMSService();
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("DependencyService:");
        DependencyService();

        Console.WriteLine();

        Console.WriteLine("InversionDependencyService:");
        InversionDependencyService();

        Console.ReadKey();
    }

    static void DependencyService()
    {
        EmailService fooEmailService = new EmailService();
        fooEmailService.Send();
    }
    static void InversionDependencyService()
    {
        IMessage fooMessage = Factory.GetEmailService();
        fooMessage.Send();
        fooMessage = Factory.GetSMSService();
        fooMessage.Send();
    }
}

在这个例子中,我们通过工厂模式创建具体的服务,然后供主程序来调用,代码依然很简单,分别用传统创建服务的方式和 控制反转的方式来进行对比。程序输出如下

img_e8d592dbb8237894f7c21b837f08fbca.png

构建入门级 MEF

有了上面两个知识点做铺垫,我们可以开始创建一个入门级的 MEF 示例程序。想要在程序中使用 MEF 的话需要引入如下程序集

  • System.ComponentModel.Composition

这里还是以控制台程序来展示。项目结构如下图所示

img_57cff2895a62d657e0d20a6c64a7ca91.png
  • MefSample.Core:核心接口定义在该项目中
  • MefSample.EmailService:插件,需要引用 MefSample.Core 和 System.ComponentModel.Composition
  • MefSample.SMSService:插件,需要引用 MefSample.Core 和 System.ComponentModel.Composition
  • MefSample:主程序,需要引用 MefSample.Core 和 System.ComponentModel.Composition

注意:上述所有项目程序的输出目录需要保持一致

MefSample.Core 代码段

public interface IMessage
{
    void Send();
}

MefSample.EmailService 代码段

[Export(typeof(IMessage))]
public class EmailService: IMessage
{
    public void Send()
    {
        Console.WriteLine("Email Send Message");
    }
}

MefSample.SMSService 代码段

[Export(typeof(IMessage))]
public class SMSService : IMessage
{
    public void Send()
    {
        Console.WriteLine("SMS Send Message");
    }
}

MefSample 代码段

class Program
{
    static void Main(string[] args)
    {
        var dir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);
        var catalog = new DirectoryCatalog(dir.FullName, "*.dll");
        using (CompositionContainer container = new CompositionContainer(catalog))
        {
            IEnumerable<IMessage> messages = container.GetExportedValues<IMessage>();
            if (messages != null)
            {
                foreach (var message in messages)
                {
                    message.Send();
                }
            }
        }
        Console.ReadKey();
    }
}

仔细观察的话,其实上述代码还是挺简单的, 我这里使用了 DirectoryCatalog 的方式来寻找目标插件,感兴趣的朋友可以试试其他方式:AggregateCatalogAssemblyCatalogDirectoryCatalog。当然,你也可以自定义。程序输出结果如下图所示

img_7712d4cb0b1a42e3aa544aa8dacc892e.png

好了,程序写到这里相信你对 MEF 也多少有些了解。我没有过多的讲解抽象理论,而是更多地通过代码来描述我所想要说的。在下篇文章中,我将会简单讲述一下 MEF 在 WPF 中的入门使用,方便初学者更上一层楼。加油,共勉!

相关参考

目录
相关文章
|
5月前
|
开发框架 小程序 JavaScript
基于mpvue框架的小程序项目搭建入门教程一
基于mpvue框架的小程序项目搭建入门教程一
113 0
|
缓存 前端开发 JavaScript
浅浅阅读umi中InitialState插件源码 - 杨磊
InitialState插件源码的简要介绍
824 0
浅浅阅读umi中InitialState插件源码 - 杨磊
|
缓存 前端开发 JavaScript
浅浅阅读umi中InitialState插件源码
InitialState插件源码的简要介绍
474 1
浅浅阅读umi中InitialState插件源码
|
JavaScript 前端开发 Java
Vue3 的学习教程汇总、源码解释项目、支持的 UI 组件库、优质实战项目
Vue3 的学习教程汇总、源码解释项目、支持的 UI 组件库、优质实战项目
544 0
Vue3 的学习教程汇总、源码解释项目、支持的 UI 组件库、优质实战项目
|
搜索推荐 IDE Java
pinpoint插件开发之二:从零开始新建一个插件
从零开始新建pinpoint插件,本篇给出从编码到部署运行的详细步骤
868 0
pinpoint插件开发之二:从零开始新建一个插件
|
前端开发 JavaScript .NET
【ABP框架系列学习】介绍篇(1)
原文:【ABP框架系列学习】介绍篇(1)   0.引言 该系列博文主要在【官方文档】及【tkbSimplest】ABP框架理论研究系列博文的基础上进行总结的,或许大家会质问,别人都已经翻译过了,这不是多此一举吗?原因如下: 1.【tkbSimplest】的相关博文由于撰写得比较早的,在参照官方文档学习的过程中,发现部分知识未能及时同步(当前V4.0.2版本),如【EntityHistory】、【Multi-Lingual Engities】章节未涉及、【Caching】章节没有Entity Caching等内容。
1815 0
|
测试技术 C# 图形学
Unity C#代码封装dll文件完美教程(Chinar-开发者必经之路)
Unity C#代码封装dll文件完美教程(Chinar-开发者必经之路) 将脚本封装为DLL库,本文提供全流程,中文翻译。 助力快速完成 Unity C#脚本文件封装为 DLL 库文件 DLL (Dynamic Link Library) —— 中文:动态链接库
5204 0
|
存储 缓存 前端开发
【JavaScript框架封装】自己动手封装一个涵盖JQuery基本功能的框架及核心源码分享(单文件版本)
版权声明:本文为博主原创文章,未经博主允许不得转载。更多学习资料请访问我爱科技论坛:www.52tech.tech https://blog.csdn.net/m0_37981569/article/details/81156178 ...
1599 0
|
JSON 图形学 数据格式
Unity3D插件-自制小插件、简化代码便于使用(新手至高手进阶必经之路)
Unity3D插件-简化代码、封装功能 本文提供全流程,中文翻译。Chinar坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 —— 高分辨率用户请根据需求调整网页缩放比例) ...
1211 0