C Sharp与.net学习笔记(三)

先简单了解一下C#下插件框架。插件一般就是定义了某个特定接口的并被动态加载的动态库。应用程序启动后,可以查找(比如某个特定目录)、动态加载、识别(某个特定接口)、使用插件(调用接口函数等)。

现在.net库中有了两套插件的框架:

MAF: Managed Add-in Framework

从VS2008(.NET3.5)开始

MEF: Managed Extensibility Framework

从VS2010(.NET4.0)开始

这两个的优劣,网上已经有了很多的讨论。最主要的可能就是:(fixme)

  • MAF使用比较繁琐,但可以将主程序和插件完全隔离
    • 即使某个插件崩溃了主程序也不受影响。可以动态更新和卸载插件
    • 不同版本的插件、主程序兼容性容易实现。
  • MEF使用简单,主程序和插件不需要明确的区分
    • 似乎大家都更看好这个,VS2010自身采用的也是MEF?

MAF

主程序

程序接口

 

接口

 

插件接口

插件

Host

Host views of Add-ins

 

Contracts

 

Add-In views

Add-In

 

adapters

 

adapters

   

看起来好复杂啊,但似乎还不是太难理解:

  • 对于传统方式的插件,我们定义一个接口,插件提供该接口实现,程序通过接口操作
  • 在这儿,首先定义一个接口(称为Contract),但是程序和插件都不用关心这个Contract具体是什么样子,它们只关心各自的接口(View),View和Contract之间通过适配器进行适配。

凭空多出这个多东西,而且应该程序要能找到它们,故MAF都它们的位置有要求:

类别

位置(某个共同目录的子目录)

HostSideAdapters

PathOfDbzhang\HostSideAdapters

Contracts

PathOfDbzhang\Contracts

AddInSideAdapters

PathOfDbzhang\AddInSideAdapters

AddInViews

PathOfDbzhang\AddInViews

AddIns

PathOfDbzhang\AddIns\XXXX

MAF 例子

这个东西太麻烦了,恩,抛开VS集成环境,直接使用命令行来试试一个简单的例子

源文件

==>

最终产物

说明

Application.cs

 

Application.exe

我们的程序

HostAddInView.cs

 

MyHostAddInView.dll

HostSideAdapter.cs

 

HostSideAdapters/ MyHostSideAdapter.dll

 

AddInContract.cs

 

Contracts/ MyAddInContract.dll

接口

AddInSideAdapter.cs

 

AddInSideAdapters/ MyAddInSideAdapter.dll

 

AddInView.cs

 

AddInViews/ MyAddInView.dll

 

Addin1.cs

 

AddIns/XXXX/Addin1.dll

插件

应用程序

  • 定义程序操作的接口HostAddInView.cs :

namespace CalcHVAs
{
    public abstract class Calculator
    {
        public abstract double Calc(double a, double b);
    }
}
  • 定义我们的程序 Application.cs(它只操作我们前面刚定义的接口):

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.AddIn.Hosting;
using CalcHVAs;

namespace MathHost
{
    class Program
    {
        static void Main()
        {
            // Assumes that the current directory is the addins root directory. 
            String addInRoot = Environment.CurrentDirectory;

            // Search for addins.
            AddInStore.Update(addInRoot);
            Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(Calculator), addInRoot);

            // Select an add-in.
            if (tokens.Count == 0) {
                Console.WriteLine("No plugins available.");
                return;
            }
            AddInToken calcToken = ChooseCalculator(tokens);

            //Activate the AddIn in a new application domain with the Internet trust level.
            Calculator calc = calcToken.Activate<Calculator>(AddInSecurityLevel.Internet);

            //Run the add-in.
            RunCalculator(calc);
        }

        private static AddInToken ChooseCalculator(Collection<AddInToken> tokens)
        {
            Console.WriteLine("Available Plugins: ");
            int tokNo = 0;
            foreach (AddInToken tok in tokens) {
                Console.WriteLine("\t[{0}]: {1}", tokNo.ToString(), tok.AssemblyName);
                tokNo++;
            }
            Console.WriteLine("Which calculator do you want to use?");
            String line = Console.ReadLine();
            int selection;
            if (Int32.TryParse(line, out selection)) {
                if (selection < tokens.Count) {
                    return tokens[selection];
                }
            }
            return tokens[0];
        }

        private static void RunCalculator(Calculator calc)
        {
            Console.Write("Input two numbers such as 2 3 (Or type <q> to exit). \n%> ");
            String line = Console.ReadLine();
            while (!line.Equals("q")) {
                try {
                    String[] parts = line.Split(' ');
                    double a = Double.Parse(parts[0]);
                    double b = Double.Parse(parts[1]);
                    Console.Write("{0}\n%> ", calc.Calc(a, b));
                } catch {
                    Console.Write("Invalid command: {0} \n%> ", line);
                }
                line = Console.ReadLine();
            }
        }
    }
}

插件

  • 定义插件需要实现的接口 AddInView.cs

using System;
using System.AddIn.Pipeline;

namespace CalcAddInViews
{
    [AddInBase()]
    public abstract class Calculator
    {
        public abstract double Calc(double a, double b);
    }
}
  • 实现该接口Addin1.cs

using System.AddIn;
using CalcAddInViews;

namespace CalcAddIns
{
    [AddIn("Dbzhang800 AddIn",Version="1.0.0.0")]
    public class AddInCalcV1 : Calculator
    {
        public override double Calc(double a, double b)
        {
            return a + b;
        }
    }
}

接口适配

  • 定义接口 AddInContract.cs :

using System.AddIn.Contract;
using System.AddIn.Pipeline;

namespace CalculatorContracts
{
    [AddInContract]
    public interface ICalc1Contract : IContract
    {
        double Calc(double a, double b);
    }
}

将应用程序和接口的接口和该接口分别进行适配

  • HostSideAdpater.cs

using System;
using System.AddIn.Pipeline;
using CalcHVAs;
using CalculatorContracts;

namespace CalcHostSideAdapters
{
    [HostAdapterAttribute()]
    public class CalculatorContractToViewHostSideAdapter : Calculator
    {
        private ICalc1Contract _contract;
        private System.AddIn.Pipeline.ContractHandle _handle;
    
        public CalculatorContractToViewHostSideAdapter(ICalc1Contract contract) 
        {
                _contract = contract;
                _handle = new ContractHandle(contract);
        }
    
        public override double Calc(double a, double b)
        {
                return _contract.Calc(a, b);
        }
    }
}
  • AddInSideAdapter.cs

using System;
using System.AddIn.Pipeline;
using CalcAddInViews;
using CalculatorContracts;

namespace CalcAddInSideAdapters
{
    [AddInAdapter()]
    public class CalculatorViewToContractAddInSideAdapter : ContractBase, ICalc1Contract
    {
        private Calculator _view;

        public CalculatorViewToContractAddInSideAdapter(Calculator view)
        {
            _view = view;
        }

        public virtual double Calc(double a, double b)
        {
            return _view.Calc(a, b);
        }
    }
}

编译

前面7个文件,要分别编译成7个dll/exe文件,还好,每一只需要一个csc命令即可,写成一个.bat文件,如下

set libPath="C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5"

csc /target:library /out:Contracts\MyAddInContract.dll  /lib:%libPath% /r:System.AddIn.Contract.dll;System.AddIn.dll  AddInContract.cs

csc /target:library  /out:AddInViews\MyAddInView.dll  /lib:%libPath% /r:System.AddIn.dll  AddInView.cs

csc /target:library HostAddInView.cs

csc /target:library  /out:AddInSideAdapters\MyAddInSideAdpter.dll /lib:%libPath% /r:System.AddIn.dll;System.AddIn.Contract.dll;AddInViews\MyAddInView.dll;Contracts\MyAddInContract.dll AddInSideAdapter.cs

csc /target:library  /out:HostSideAdapters\MyHostSideAdpter.dll /lib:%libPath% /r:System.AddIn.dll;System.AddIn.Contract.dll;HostAddInView.dll;Contracts\MyAddInContract.dll HostSideAdapter.cs

csc /lib:%libPath% /r:System.AddIn.dll;HostAddInView.dll Application.cs

csc /target:library  /out:AddIns\CalcV1\MyAddIn1.dll /lib:%libPath% /r:System.AddIn.dll;System.AddIn.Contract.dll;AddInViews\MyAddInView.dll AddIn1.cs

结果

看看运行结果:

E:\CalculatorV1> application
Available Plugins:
        [0]: MyAddIn1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
Which calculator do you want to use?
0
Input two numbers such as 2 3 (Or type <q> to exit).
%> 2 66
68
%> 333 221
554
%> q

猜你喜欢

转载自blog.csdn.net/dbzhang800/article/details/7203775
今日推荐