1. 程式人生 > >C Sharp與.net學習筆記(三)

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 viewsof 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