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