基于MEF打造的插件系统的实现详解

以实例说话,一起体验MEF带来的可扩展性吧,Let's Rock!!!

1:新建控制台程序SimpleCalculator

在这里要实现的程序时SimpleCalculator,顾名思义:简单的计算器。

所以我们需要定义一个用来计算的接口:

public interface ICalculator

{

String Calculate(String input);

}

Program 的代码如下:

class Program
{
    private CompositionContainer _container;

[Import(typeof(ICalculator))]
    private ICalculator calculator;

public Program()
    {
        //var catalog = new AggregateCatalog();
        //catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));

var catalog = new AssemblyCatalog(typeof(Program).Assembly);
        _container = new CompositionContainer(catalog);

try
        {
            this._container.ComposeParts(this);
        }
        catch (CompositionException compositionException)
        {
            Console.WriteLine(compositionException.ToString());
        }
    }

static void Main(string[] args)
    {
        Program p = new Program();
        string s;
        Console.WriteLine("Enter Command:");

while (true)
        {
            s = Console.ReadLine();
            Console.WriteLine(p.calculator.Calculate(s));
        }
    }
}

MEF所要解决的是寻找插件的功能,传统的实现插件的方式主要是使用接口,即声明一个接口,然后使用配置文件来配置接口使用哪个实现类。

微软知道有这种需求,于是提供了MEF来实现插件的功能。

Composite 原理

1:声明一个 CompositionContainer 对象,这个对象里面包含一堆Catalog.

2:这堆Catalog如果是AssemblyCatalog,则在Assembly中查找,如果是DirectoryCatalog,

在Directory 中查找,如果即想要在Assembly中查找,又需要在Directory中查找,

则采用AggregateCatalog。

3:然后在这堆Catalog中查找与Import 特性相对应的Export标记所标记的实现类,调用实现类的构造函数进行

Composite(组合)。

知道原理后,你也可以自己实现自己的CompositionContainer 类了,

要使用MEF 需要为SimpleCalculator添加 System.ComponentModel.Composition.dll 的引用,

然后导入命名空间:

using System.ComponentModel.Composition;

using System.ComponentModel.Composition.Hosting;

接下来看下Program 的构造函数所做的事情:

声明一个AssemblyCatalog,指向Program所在的Assembly. 然后把它添加到

CompositionContainer中,调用CompositionContainer 的ComposeParts 扩展方法,来Compose(this) 的Parts。

注:ComposeParts 是扩展方法,需要using System.ComponentModel.Composition;

OK,如何Compose,在哪个Assembly中查找实现类来进行Compose已经完成了。

目前的问题是:哪些类需要Compose??

为了回答这个问题,微软提供了Import和Export特性:

Import:哪个对象需要Compose。也就是需要被实现类给填充,所以Import标记的是对象,一般该对象是接口,因为如果是具体类的话,那还需要Import吗?

Export:哪个类可以被用来Compose,也就是说这个类是不是可以用来填充的实现类,所以Export标记的是类,而不是具体的某个对象。

所以在这里calculator 使用Import 特性来标记:

[Import(typeof(ICalculator))]

private ICalculator calculator;

接下来MEF 的组合引擎在ComposeParts(this)的时候,就会在catalog 代表的AssemblyCatalog中查找Export特性所修饰的实现类了,找到实现类后进行Compose。

如果找不到Export特性修饰的类的话,结果如下:

OK,接下来添加一个实现类,并使用Export特性来进行修饰:

[Export(typeof(ICalculator))]

public class MySimpleCalculator : ICalculator

{

public string Calculate(string input)

{

return "MySimpleCalculator 处理了" + input;

}

}

运行结果如下:

当然Import和Export还提供了其他的构造函数,所以你还可以将上面的Import和Export修改为:

[Import("calculator1", typeof(ICalculator))]

[Export("calculator1", typeof(ICalculator))]

之所以提供ContractName为calculator1 是因为你可能有多个ICalculator对象需要填充。

修改Program的代码如下:

class Program

{

private CompositionContainer _container;

[Import("calculator1", typeof(ICalculator))]

private ICalculator calculator1;

[Import("calculator2", typeof(ICalculator))]

private ICalculator calculator2;

public Program()

{

//var catalog = new AggregateCatalog();

//catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));

var catalog = new AssemblyCatalog(typeof(Program).Assembly);

_container = new CompositionContainer(catalog);

try

{

this._container.ComposeParts(this);

}

catch(CompositionException compositionException)

{

Console.WriteLine(compositionException.ToString());

}

}

static void Main(string[] args)

{

Program p = new Program();

string s;

Console.WriteLine("Enter Command:");

while (true)

{

s = Console.ReadLine();

Console.WriteLine(p.calculator1.Calculate(s));

Console.WriteLine(p.calculator2.Calculate(s));

}

}

}

修改Export修饰的类为:

[Export("calculator1", typeof(ICalculator))]

public class MySimpleCalculator1 : ICalculator

{

public string Calculate(string input)

{

return "第一个Calculator 处理了" + input;

}

}

[Export("calculator2", typeof(ICalculator))]

public class MySimpleCalculator2 : ICalculator

{

public string Calculate(string input)

{

return "第二个Calculator 处理了" + input;

}

}

运行结果如下:

因为Import和Export是一一对应的,在现实世界中,存在着大量一对多的情况,微软也预料到了这种情况,所以提供了ImportMany 特性。

在上个例子中的MySimpleCalculator的Calculate方法返回的是一句话,在这个例子中要真正实现计算的功能,例如输入5+3,输出8,输入7*4,输出28。

为了支持 + - * / 四种Operation.所以在MySimpleCalculator中声明一个operations 的列表。

[Export(typeof(ICalculator))]

class MySimpleCalculator : ICalculator

{

[ImportMany]

IEnumerable<Lazy<IOperation, IOperationData>> operations;

public string Calculate(string input)

{

return "calculate 处理了" + input;

}

}

之所以在MySimpleCalculator 中声明operations ,是因为是计算器支持多种运算。因为operations 需要多个operation 来Compose(填充),所以使用ImportMany特性来修饰,和Import特性一样,ImportMany特性一般也是修饰接口。

Ioperation 和IOperationData的定义如下:

public interface IOperation

{

int Operate(int left, int right);

}

public interface IOperationData

{

Char Symbol { get; }

}

Lazy<IOperation, IOperationData> operations:

提供对对象及其关联的元数据的延迟间接引用,以供 Managed Extensibility Framework 使用。

意思是说IOperation 和IOperationData之间的引用需要延迟,为什么需要延迟?,因为IOperation需要根据IOperationData的Symbol符号来延迟创建。

也就是说,如果IOperationData的Symbol 等于 “+”,那么IOperation对象是AddOperation.如果IOperationData的Symbol等于”-”,那么IOperation对象是SubtractOperation.

那么如何保证这点呢?

关键点就在于ExportMetadata attribute 上。

看下Add Operation 的定义:

[Export(typeof(IOperation))]

[ExportMetadata("Symbol", '+')]

class Add : IOperation

{

public int Operate(int left, int right)

{

return left + right;

}

}

在这里ExportMetadata特性的Symbol 为+。所以当IOperationData的Symbol为”+” 的时候,匹配的就是Add Operation

MySimpleCalculator 的完整代码如下:

[Export(typeof(ICalculator))]

class MySimpleCalculator : ICalculator

{

[ImportMany]

IEnumerable<Lazy<IOperation, IOperationData>> operations;

public string Calculate(string input)

{

int left;

int right;

char operation;

int fn = FindFirstNonDigitPosition(input);

if (fn < 0) return "Could not parse command.";

try

{

left = int.Parse(input.Substring(0, fn));

right = int.Parse(input.Substring(fn + 1));

}

catch

{

return "Could not parse command";

}

operation = input[fn];

foreach (Lazy<IOperation, IOperationData> i in operations)

{

if (i.Metadata.Symbol.Equals(operation))

return i.Value.Operate(left, right).ToString();

}

return "Operation Not Found!";

}

private int FindFirstNonDigitPosition(string s)

{

for (int i = 0; i < s.Length; i++)

{

if (!(Char.IsDigit(s[i]))) return i;

}

return -1;

}

}

回头再看看上例的Program代码:

class Program

{

private CompositionContainer _container;

[Import(typeof(ICalculator))]

private ICalculator calculator;

public Program()

{

//var catalog = new AggregateCatalog();

//catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));

var catalog = new AssemblyCatalog(typeof(Program).Assembly);

_container = new CompositionContainer(catalog);

try

{

this._container.ComposeParts(this);

}

catch(CompositionException compositionException)

{

Console.WriteLine(compositionException.ToString());

}

}

static void Main(string[] args)

{

Program p = new Program();

string s;

Console.WriteLine("Enter Command:");

while (true)

{

s = Console.ReadLine();

Console.WriteLine(p.calculator.Calculate(s));

}

}

}

当this._container.ComposeParts(this); 的时候,MEF组合引擎就开始对标记了Import特性的接口进行Compose,所以在这里是calculator。在哪里找实现类呢?,AssemblyCatalog表明在Program的当前Assembly中查找实现类,所以找到了MySimpleCalculator在构造MySimpleCalculator 的时候,发现了ImportMany特性修饰的operations。于是继续在AssemblyCatalog中找到了Add。

上面的过程是Compose的过程。

那么MySimpleCalculator 如何进行Calculate的呢?

例如5+3

1:找出第一个非数字的位置,也就是需要找出 +。

2:声明left,right.并且left 为5,right为3.

3:根据符号+来构造IOperation对象,接着调用IOperation对象的Operate(left,right)方法。

foreach (Lazy<IOperation, IOperationData> i in operations)

{

if (i.Metadata.Symbol.Equals(operation))

return i.Value.Operate(left, right).ToString();

}

运行结果:

因为目前定义了Add 的Operation。所以根据符号+ 能够找到Add,但是*我们没有定义,所以Operation Not Found!.

于是开始定义Multiple:

[Export(typeof(IOperation))]

[ExportMetadata("Symbol", '*')]

class Multiple : IOperation

{

public int Operate(int left, int right)

{

return left * right;

}

}

再次运行,结果如下:

当然还可以在当前程序集下面增加- ,/,^,% 等Operation。

为了让事情更加的有趣,我打算在Debug目录下增加一个目录CalculateExtensions,然后将-,/ ..的Operation放到里面来,让MEF自动发现。

首先新建类库项目:SimpleCalculatorExtension

因为需要实现IOperation ,所以需要添加对SimpleCalculator项目的引用。

因为需要Export特性,所以需要添加对System.ComponentModel.Composition的引用。

整个项目的结果如下:

Subtract代码如下:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.ComponentModel.Composition;

namespace SimpleCalculatorExtension

{

[Export(typeof(SimpleCalculator.IOperation))]

[ExportMetadata("Symbol", '-')]

class Subtract : SimpleCalculator.IOperation

{

public int Operate(int left, int right)

{

return left - right;

}

}

}

生成成功后,将SimpleCalculatorExtension.dll 拷贝到CalculateExtensions目录下:

现在SimpleCalculator的Debug目录应该是这样。

并且CalculateExtensions文件夹下面有SimpleCalculatorExtension.dll.

接下来唯一要修改的是Program的catalog 对象。

为了让catalog既支持在Program的Assembly中查找,又支持在CalculateExtensions目录下查找。修改代码如下:

public Program()

{

var catalog = new AggregateCatalog();

catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));

catalog.Catalogs.Add(new DirectoryCatalog("CalculateExtensions"));

_container = new CompositionContainer(catalog);

try

{

this._container.ComposeParts(this);

}

catch(CompositionException compositionException)

{

Console.WriteLine(compositionException.ToString());

}

}

运行结果如下:

修改SimpleCalculatorExtension 的Subtract方法为:

namespace SimpleCalculatorExtension

{

[Export(typeof(SimpleCalculator.IOperation))]

[ExportMetadata("Symbol", '-')]

class Subtract : SimpleCalculator.IOperation

{

public int Operate(int left, int right)

{

Console.WriteLine("SimpleCalculatorExtension的方法");

return left - right;

}

}

}

重新生成SimpleCalculatorExtension.dll 然后拷贝到CalculateExtensions 文件夹下:

再次运行程序,输出入下:

文章有点长,而且有点乱,最好自己动手实践下MEF,不过讲的都是MEF的基础,希望对你有所帮助,另外如果你不使用MEF,采用面向接口的编程原则的话,相信你自己也很容易实现自己的“MEF”

】:

以实例说话,一起体验MEF带来的可扩展性吧,Let's Rock!!!

1:新建控制台程序SimpleCalculator

在这里要实现的程序时SimpleCalculator,顾名思义:简单的计算器。

所以我们需要定义一个用来计算的接口:

public interface ICalculator

{

String Calculate(String input);

}

Program 的代码如下:

class Program
{
    private CompositionContainer _container;

[Import(typeof(ICalculator))]
    private ICalculator calculator;

public Program()
    {
        //var catalog = new AggregateCatalog();
        //catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));

var catalog = new AssemblyCatalog(typeof(Program).Assembly);
        _container = new CompositionContainer(catalog);

try
        {
            this._container.ComposeParts(this);
        }
        catch (CompositionException compositionException)
        {
            Console.WriteLine(compositionException.ToString());
        }
    }

static void Main(string[] args)
    {
        Program p = new Program();
        string s;
        Console.WriteLine("Enter Command:");

while (true)
        {
            s = Console.ReadLine();
            Console.WriteLine(p.calculator.Calculate(s));
        }
    }
}

MEF所要解决的是寻找插件的功能,传统的实现插件的方式主要是使用接口,即声明一个接口,然后使用配置文件来配置接口使用哪个实现类。

微软知道有这种需求,于是提供了MEF来实现插件的功能。

Composite 原理

1:声明一个 CompositionContainer 对象,这个对象里面包含一堆Catalog.

2:这堆Catalog如果是AssemblyCatalog,则在Assembly中查找,如果是DirectoryCatalog,

在Directory 中查找,如果即想要在Assembly中查找,又需要在Directory中查找,

则采用AggregateCatalog。

3:然后在这堆Catalog中查找与Import 特性相对应的Export标记所标记的实现类,调用实现类的构造函数进行

Composite(组合)。

知道原理后,你也可以自己实现自己的CompositionContainer 类了,

要使用MEF 需要为SimpleCalculator添加 System.ComponentModel.Composition.dll 的引用,

然后导入命名空间:

using System.ComponentModel.Composition;

using System.ComponentModel.Composition.Hosting;

接下来看下Program 的构造函数所做的事情:

声明一个AssemblyCatalog,指向Program所在的Assembly. 然后把它添加到

CompositionContainer中,调用CompositionContainer 的ComposeParts 扩展方法,来Compose(this) 的Parts。

注:ComposeParts 是扩展方法,需要using System.ComponentModel.Composition;

OK,如何Compose,在哪个Assembly中查找实现类来进行Compose已经完成了。

目前的问题是:哪些类需要Compose??

为了回答这个问题,微软提供了Import和Export特性:

Import:哪个对象需要Compose。也就是需要被实现类给填充,所以Import标记的是对象,一般该对象是接口,因为如果是具体类的话,那还需要Import吗?

Export:哪个类可以被用来Compose,也就是说这个类是不是可以用来填充的实现类,所以Export标记的是类,而不是具体的某个对象。

所以在这里calculator 使用Import 特性来标记:

[Import(typeof(ICalculator))]

private ICalculator calculator;

接下来MEF 的组合引擎在ComposeParts(this)的时候,就会在catalog 代表的AssemblyCatalog中查找Export特性所修饰的实现类了,找到实现类后进行Compose。

如果找不到Export特性修饰的类的话,结果如下:

OK,接下来添加一个实现类,并使用Export特性来进行修饰:

[Export(typeof(ICalculator))]

public class MySimpleCalculator : ICalculator

{

public string Calculate(string input)

{

return "MySimpleCalculator 处理了" + input;

}

}

运行结果如下:

当然Import和Export还提供了其他的构造函数,所以你还可以将上面的Import和Export修改为:

[Import("calculator1", typeof(ICalculator))]

[Export("calculator1", typeof(ICalculator))]

之所以提供ContractName为calculator1 是因为你可能有多个ICalculator对象需要填充。

修改Program的代码如下:

class Program

{

private CompositionContainer _container;

[Import("calculator1", typeof(ICalculator))]

private ICalculator calculator1;

[Import("calculator2", typeof(ICalculator))]

private ICalculator calculator2;

public Program()

{

//var catalog = new AggregateCatalog();

//catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));

var catalog = new AssemblyCatalog(typeof(Program).Assembly);

_container = new CompositionContainer(catalog);

try

{

this._container.ComposeParts(this);

}

catch(CompositionException compositionException)

{

Console.WriteLine(compositionException.ToString());

}

}

static void Main(string[] args)

{

Program p = new Program();

string s;

Console.WriteLine("Enter Command:");

while (true)

{

s = Console.ReadLine();

Console.WriteLine(p.calculator1.Calculate(s));

Console.WriteLine(p.calculator2.Calculate(s));

}

}

}

修改Export修饰的类为:

[Export("calculator1", typeof(ICalculator))]

public class MySimpleCalculator1 : ICalculator

{

public string Calculate(string input)

{

return "第一个Calculator 处理了" + input;

}

}

[Export("calculator2", typeof(ICalculator))]

public class MySimpleCalculator2 : ICalculator

{

public string Calculate(string input)

{

return "第二个Calculator 处理了" + input;

}

}

运行结果如下:

因为Import和Export是一一对应的,在现实世界中,存在着大量一对多的情况,微软也预料到了这种情况,所以提供了ImportMany 特性。

在上个例子中的MySimpleCalculator的Calculate方法返回的是一句话,在这个例子中要真正实现计算的功能,例如输入5+3,输出8,输入7*4,输出28。

为了支持 + - * / 四种Operation.所以在MySimpleCalculator中声明一个operations 的列表。

[Export(typeof(ICalculator))]

class MySimpleCalculator : ICalculator

{

[ImportMany]

IEnumerable<Lazy<IOperation, IOperationData>> operations;

public string Calculate(string input)

{

return "calculate 处理了" + input;

}

}

之所以在MySimpleCalculator 中声明operations ,是因为是计算器支持多种运算。因为operations 需要多个operation 来Compose(填充),所以使用ImportMany特性来修饰,和Import特性一样,ImportMany特性一般也是修饰接口。

Ioperation 和IOperationData的定义如下:

public interface IOperation

{

int Operate(int left, int right);

}

public interface IOperationData

{

Char Symbol { get; }

}

Lazy<IOperation, IOperationData> operations:

提供对对象及其关联的元数据的延迟间接引用,以供 Managed Extensibility Framework 使用。

意思是说IOperation 和IOperationData之间的引用需要延迟,为什么需要延迟?,因为IOperation需要根据IOperationData的Symbol符号来延迟创建。

也就是说,如果IOperationData的Symbol 等于 “+”,那么IOperation对象是AddOperation.如果IOperationData的Symbol等于”-”,那么IOperation对象是SubtractOperation.

那么如何保证这点呢?

关键点就在于ExportMetadata attribute 上。

看下Add Operation 的定义:

[Export(typeof(IOperation))]

[ExportMetadata("Symbol", '+')]

class Add : IOperation

{

public int Operate(int left, int right)

{

return left + right;

}

}

在这里ExportMetadata特性的Symbol 为+。所以当IOperationData的Symbol为”+” 的时候,匹配的就是Add Operation

MySimpleCalculator 的完整代码如下:

[Export(typeof(ICalculator))]

class MySimpleCalculator : ICalculator

{

[ImportMany]

IEnumerable<Lazy<IOperation, IOperationData>> operations;

public string Calculate(string input)

{

int left;

int right;

char operation;

int fn = FindFirstNonDigitPosition(input);

if (fn < 0) return "Could not parse command.";

try

{

left = int.Parse(input.Substring(0, fn));

right = int.Parse(input.Substring(fn + 1));

}

catch

{

return "Could not parse command";

}

operation = input[fn];

foreach (Lazy<IOperation, IOperationData> i in operations)

{

if (i.Metadata.Symbol.Equals(operation))

return i.Value.Operate(left, right).ToString();

}

return "Operation Not Found!";

}

private int FindFirstNonDigitPosition(string s)

{

for (int i = 0; i < s.Length; i++)

{

if (!(Char.IsDigit(s[i]))) return i;

}

return -1;

}

}

回头再看看上例的Program代码:

class Program

{

private CompositionContainer _container;

[Import(typeof(ICalculator))]

private ICalculator calculator;

public Program()

{

//var catalog = new AggregateCatalog();

//catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));

var catalog = new AssemblyCatalog(typeof(Program).Assembly);

_container = new CompositionContainer(catalog);

try

{

this._container.ComposeParts(this);

}

catch(CompositionException compositionException)

{

Console.WriteLine(compositionException.ToString());

}

}

static void Main(string[] args)

{

Program p = new Program();

string s;

Console.WriteLine("Enter Command:");

while (true)

{

s = Console.ReadLine();

Console.WriteLine(p.calculator.Calculate(s));

}

}

}

当this._container.ComposeParts(this); 的时候,MEF组合引擎就开始对标记了Import特性的接口进行Compose,所以在这里是calculator。在哪里找实现类呢?,AssemblyCatalog表明在Program的当前Assembly中查找实现类,所以找到了MySimpleCalculator在构造MySimpleCalculator 的时候,发现了ImportMany特性修饰的operations。于是继续在AssemblyCatalog中找到了Add。

上面的过程是Compose的过程。

那么MySimpleCalculator 如何进行Calculate的呢?

例如5+3

1:找出第一个非数字的位置,也就是需要找出 +。

2:声明left,right.并且left 为5,right为3.

3:根据符号+来构造IOperation对象,接着调用IOperation对象的Operate(left,right)方法。

foreach (Lazy<IOperation, IOperationData> i in operations)

{

if (i.Metadata.Symbol.Equals(operation))

return i.Value.Operate(left, right).ToString();

}

运行结果:

因为目前定义了Add 的Operation。所以根据符号+ 能够找到Add,但是*我们没有定义,所以Operation Not Found!.

于是开始定义Multiple:

[Export(typeof(IOperation))]

[ExportMetadata("Symbol", '*')]

class Multiple : IOperation

{

public int Operate(int left, int right)

{

return left * right;

}

}

再次运行,结果如下:

当然还可以在当前程序集下面增加- ,/,^,% 等Operation。

为了让事情更加的有趣,我打算在Debug目录下增加一个目录CalculateExtensions,然后将-,/ ..的Operation放到里面来,让MEF自动发现。

首先新建类库项目:SimpleCalculatorExtension

因为需要实现IOperation ,所以需要添加对SimpleCalculator项目的引用。

因为需要Export特性,所以需要添加对System.ComponentModel.Composition的引用。

整个项目的结果如下:

Subtract代码如下:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.ComponentModel.Composition;

namespace SimpleCalculatorExtension

{

[Export(typeof(SimpleCalculator.IOperation))]

[ExportMetadata("Symbol", '-')]

class Subtract : SimpleCalculator.IOperation

{

public int Operate(int left, int right)

{

return left - right;

}

}

}

生成成功后,将SimpleCalculatorExtension.dll 拷贝到CalculateExtensions目录下:

现在SimpleCalculator的Debug目录应该是这样。

并且CalculateExtensions文件夹下面有SimpleCalculatorExtension.dll.

接下来唯一要修改的是Program的catalog 对象。

为了让catalog既支持在Program的Assembly中查找,又支持在CalculateExtensions目录下查找。修改代码如下:

public Program()

{

var catalog = new AggregateCatalog();

catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));

catalog.Catalogs.Add(new DirectoryCatalog("CalculateExtensions"));

_container = new CompositionContainer(catalog);

try

{

this._container.ComposeParts(this);

}

catch(CompositionException compositionException)

{

Console.WriteLine(compositionException.ToString());

}

}

运行结果如下:

修改SimpleCalculatorExtension 的Subtract方法为:

namespace SimpleCalculatorExtension

{

[Export(typeof(SimpleCalculator.IOperation))]

[ExportMetadata("Symbol", '-')]

class Subtract : SimpleCalculator.IOperation

{

public int Operate(int left, int right)

{

Console.WriteLine("SimpleCalculatorExtension的方法");

return left - right;

}

}

}

重新生成SimpleCalculatorExtension.dll 然后拷贝到CalculateExtensions 文件夹下:

再次运行程序,输出入下:

文章有点长,而且有点乱,最好自己动手实践下MEF,不过讲的都是MEF的基础,希望对你有所帮助,另外如果你不使用MEF,采用面向接口的编程原则的话,相信你自己也很容易实现自己的“MEF”

(0)

相关推荐

  • 基于MEF打造的插件系统的实现详解

    以实例说话,一起体验MEF带来的可扩展性吧,Let's Rock!!! 1:新建控制台程序SimpleCalculator 在这里要实现的程序时SimpleCalculator,顾名思义:简单的计算器. 所以我们需要定义一个用来计算的接口: public interface ICalculator { String Calculate(String input); } Program 的代码如下: class Program {     private CompositionContainer

  • 基于python实现垂直爬虫系统的方法详解

    html_downloader from urllib import request def download(url): if url is None: return response = request.urlopen(url) if response.getcode() != 200: return None return response.read() html_outeputer data_list = [] def collect_data(data): data_list.appe

  • 基于PowerShell在Ubuntu系统的使用详解

    本文主要介绍如何在Ubuntu 16.04 LTS上安装和使用PowerShell.要知道,PowerShell Core是微软公司推出的一个跨平台(Windows,Linux和macOS)自动化和配置工具/框架,可与现有工具很好地配合使用,并对结构化数据(如JSON, CSV,XML等),REST API和对象模型的处理做了优化.PowerShell包括一个命令行shell,一个相关的脚本语言和一个处理cmdlet的框架. 下面先介绍在Ubuntu 16.04(Xenial Xerus)服务器

  • 使用Go基于WebSocket构建千万级视频直播弹幕系统的代码详解

    (1)业务复杂度介绍 开门见山,假设一个直播间同时500W人在线,那么1秒钟1000条弹幕,那么弹幕系统的推送频率就是: 500W * 1000条/秒=50亿条/秒 ,想想B站2019跨年晚会那次弹幕系统得是多么的NB,况且一个大型网站不可能只有一个直播间! 使用Go做WebSocket开发无非就是三种情况: 使用Go原生自带的库,也就是 golang.org/x/net ,但是这个官方库真是出了奇Bug多 使用GitHub大佬 gorilla/websocket 库,可以结合到某些Web开发框

  • 基于PHP+Mysql简单实现了图书购物车系统的实例详解

    PHP+Mysql简单实现了图书购物车 本文主要讲述如何通过PHP+HTML简单实现图书购物车的功能,这是提取我们php项目的部分内容.主要内容包括: 1.通过JavaScript和Iframe实现局部布局界面     2.PHP如何定义类实现访问数据库功能     3.实现简单的添加购物车功能     4.实现了后台管理前台的页面     由于这个项目是在期末完成,PHP只是刚学的,比较简单. 效果图如下: 这是后台管理的页面: 这是前台页面: index.php页面: <!DOCTYPE h

  • jQuery.Validate表单验证插件的使用示例详解

    jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证变得更简单,同时提供了大量的定制选项,满足应用程序各种需求. 请在这里查看示例 validate示例 示例包含 验证错误时,显示红色错误提示 自定义验证规则 引入中文错误提示 重置表单需要执行2句话 源码示例 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <

  • 基于Java中throw和throws的区别(详解)

    系统自动抛出的异常 所有系统定义的编译和运行异常都可以由系统自动抛出,称为标准异常,并且 Java 强烈地要求应用程序进行完整的异常处理,给用户友好的提示,或者修正后使程序继续执行. 语句抛出的异常 用户程序自定义的异常和应用程序特定的异常,必须借助于 throws 和 throw 语句来定义抛出异常. throw是语句抛出一个异常. 语法:throw (异常对象); throw e; throws是方法可能抛出异常的声明.(用在声明方法时,表示该方法可能要抛出异常) 语法:[(修饰符)](返回

  • Java基于享元模式实现五子棋游戏功能实例详解

    本文实例讲述了Java基于享元模式实现五子棋游戏功能.分享给大家供大家参考,具体如下: 一.模式定义 享元模式,以共享的方式高效地支持大量的细粒度对象.通过复用内存中已存在的对象,降低系统创建对象实例的性能消耗.享元的英文是Flyweight,表示特别小的对象,即细粒度对象. 二.模式举例 1. 模式分析 我们借用五子棋游戏来说明这一模式. 2. 享元模式静态类图 3. 代码示例 3.1 创建抽象棋子一AbstractChessman package com.demo.flyweight.obj

  • 基于python if 判断选择结构的实例详解

    代码执行结构为顺序结构.选择结构.循环结构. python判断选择结构[if] if 判断条件 #进行判断条件满足之后执行下方语句 执行语句 elif 判断条件 #在不满足上面所有条件基础上进行条件筛选匹配之后执行下方语句 执行语句 else #再不满足上面所有的添加下执行下方语句 执行语句 下面举一个简单的例子,看兜里有多少钱来决定吃什么饭. douliqian=2 if douliqian>200: print("小龙虾走起!!0.0") elif douliqian>

  • vue 使用axios 数据请求第三方插件的使用教程详解

    axios 基于http客户端的promise,面向浏览器和nodejs 特色 •浏览器端发起XMLHttpRequests请求 •node端发起http请求 •支持Promise API •监听请求和返回 •转化请求和返回 •取消请求 •自动转化json数据 •客户端支持抵御 安装 使用npm: $ npm i axiso 为了解决post默认使用的是x-www-from-urlencoded 去请求数据,导致请求参数无法传递到后台,所以还需要安装一个插件QS $ npm install qs

随机推荐