之前面試有問道依賴注入,因為一直是做客戶端的發開發,沒有接觸這個,後邊工作接觸到了MEF,順便熟悉一下依賴注入
詳細的概念解釋就不講了,網上一大把,個人覺著依賴注入本質是為了解耦,方便擴充套件
依賴注入的方式:屬性注入和建構函式注入,還有介面注入的,看了下跟屬性注入差不多·就不展示了
上程式碼:
public interface ICalc { double Calc(double a, double b); } public class AddCalc:ICalc { public double Calc(double a, double b) { return a + b; } } public class SubtractCalc:ICalc { public double Calc(double a, double b) { return a - b; } } public class MyClac { ICalc _calc; //屬性注入 public ICalc Calc { get { return _calc; } set { _calc = value; } } //建構函式注入 public MyClac(ICalc calc) { _calc = calc; } public double Calculate(double a, double b) { return _calc.Calc(a, b); } }
(DI )依賴注入是實現(IOC)控制反轉的一種方式,但是使用的時候,比如再擴充套件的時候還是需要修改呼叫程式碼,所以就有了IOC 容器來方便這個呼叫
.NET 下邊 MEF框架就是幹這個的, 本質是通過特性和反射在執行的時候程式集動態載入。
//介面宣告 //最終呼叫過程介面 public interface ICalculator { string Calculate(String input); } //過程中操作介面 [InheritedExport]//這裡特性標識子類會被匯出,後邊子類可以不用表示export匯出特性 public interface IOperation { int Operate(int left, int right); } //這裡定義匯出操作名稱,可以用來在匯出的操作中進行篩選識別,這個介面不用實現 public interface IOperationData { string Symbol { get; } }
上邊是介面宣告,下邊實現這些介面
[Export(typeof(IOperation))] [ExportMetadata("Symbol", `+`)] public class Add : IOperation { public int Operate(int left, int right) { return left + right; } } [Export(typeof(IOperation))] [ExportMetadata("Symbol", `-`)] public class Subtract : IOperation { public int Operate(int left, int right) { return left - right; } } [Export(typeof(IOperation))] [ExportMetadata("Symbol",`/`)] public class Except : IOperation { public int Operate(int left, int right) { return left / right; } } [Export(typeof(ICalculator))] class MyCalculator : ICalculator { [ImportMany(AllowRecomposition = true)] IEnumerable<Lazy<IOperation, IOperationData>> operations; public string Calculate(string input) { int left; int right; char operation; int fn = FindFirstNonDigit(input); //finds the operator if (fn < 0) return "Could not parse command."; try { //separate out the operands 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 FindFirstNonDigit(String s) { for (int i = 0; i < s.Length; i++) { if (!(Char.IsDigit(s[i]))) return i; } return -1; } }
這裡因為加了exportmetadata特性,所以繼承類要加上export特性,不然MEF 好像不識別,如果沒有exportmetadata,只需要在介面上邊加上inheritedExport特性就可以了· MEF會自動匯入匯出的
這裡是匯出,下邊看怎麼匯入使用
private CompositionContainer _container; //這個是容器 [Import(typeof(ICalculator))] public ICalculator calculator; //這個匯入的類 private Program() { var catalog = new AggregateCatalog(); catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));//這裡直接匯入本程式集內的類 catalog.Catalogs.Add(new DirectoryCatalog("Extensions", "MEF_Ex.dll"));//這裡匯入指定目錄下的DLL,可以設定篩選項或者不設定,把目錄下所有的dll全部匯入 _container = new CompositionContainer(catalog); try { this._container.ComposeParts(this); } catch (CompositionException ex) { Console.WriteLine(ex.ToString()); } }
這裡MEF_Ex.dll是另外一個專案,生成的程式集,放到主程式目錄下Extensions目錄下即可
實現了一個類:
[Export(typeof(IOperation))] [ExportMetadata("Symbol", `%`)] public class Mod : MEF_Interface.IOperation { public int Operate(int left, int right) { return left % right; } }
在main函式中直接new program即可呼叫calc的方法
Program pro = new Program(); Console.WriteLine(pro.calculator.Calculate("1-2"));
還可以單獨匯出類的方法和屬性,以及通過metadata篩選匯入的類
完整程式碼如下:
[InheritedExport] interface IBookService { string BookName { get; set; } string GetBookName(); } // [Export("MusicBook",typeof(IBookService))] class MusicBook : IBookService { public string BookName { get; set; } [Export(typeof(string))] public string _publicBookName = "publicBookName"; [Export(typeof(string))] private string _privateBookName = "privateBookName"; public string GetBookName() { return "MusicBook"; } } // [Export("MusicBook", typeof(IBookService))] class MathBook : IBookService { public string BookName { get; set; } [Export(typeof(Func<string>))] public string GetBookName() { return "MathBook"; } [Export(typeof(Func<int,string>))] private string privateGetName(int count) { return $"get {count} MathBook"; } } // [Export("MusicBook", typeof(IBookService))] class HistoryBook : IBookService { public string BookName { get; set; } public string GetBookName() { return "HistoryBook"; } } [InheritedExport] public interface IPlugin { string Caption { get; } void Do(); } public interface IPluginMark { string Mark { get; } } [Export(typeof(IPlugin))] [ExportMetadata("Mark", "Plugin1")] public class Plugin1 : IPlugin { public string Caption { get { return "Plugin1"; } } public void Do() { Console.WriteLine("Plugin1 do"); } } [Export(typeof(IPlugin))] [ExportMetadata("Mark", "Plugin2")] public class Plugin2 : IPlugin { public string Caption { get { return "Plugin2"; } } public void Do() { Console.WriteLine("Plugin2 do"); } } [Export(typeof(IPlugin))] [ExportMetadata("Mark", "Plugin2")] public class Plugin3 : IPlugin { public string Caption { get { return "Plugin3"; } } public void Do() { Console.WriteLine("Plugin3 do"); } } #endregion class Program { #region [ImportMany] public IEnumerable<IBookService> Services { get; set; }//匯入類 [ImportMany] public List<string> InputString { get; set; }//匯入屬性 [Import] public Func<string> methodWithoutPara { get; set; }//匯入方法 [Import] public Func<int, string> methodWithPara { get; set; }//匯入方法 [ImportMany] public IEnumerable< Lazy<IPlugin, IPluginMark>> Plugins { get; set; } #endregion private CompositionContainer _container; [Import(typeof(ICalculator))] public ICalculator calculator; private Program() { var catalog = new AggregateCatalog(); catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));//匯出本程式集 catalog.Catalogs.Add(new DirectoryCatalog("Extensions", "MEF_Ex.dll"));//通過檔案匯入 _container = new CompositionContainer(catalog); try { this._container.ComposeParts(this); } catch (CompositionException ex) { Console.WriteLine(ex.ToString()); } } static void Main(string[] args) { Program pro = new Program(); Console.WriteLine(pro.calculator.Calculate("1-2")); var plugins = pro.Plugins;//.Where(v => v.Metadata.Mark == "Plugin2").ToList();//這裡可以做篩選 foreach (var p in plugins) { p.Value.Do(); } if (pro.Services != null) { foreach (var service in pro.Services) { Console.WriteLine(service.GetBookName()); } foreach (var str in pro.InputString) { Console.WriteLine(str); } //呼叫無引數的方法 if (pro.methodWithoutPara != null) { Console.WriteLine(pro.methodWithoutPara()); } //呼叫有引數的方法 if (pro.methodWithPara != null) { Console.WriteLine(pro.methodWithPara(5)); } } Console.ReadLine(); } }
總結:
1 MEF會自動匯入對應的類實現,然後自動初始化,但是具體什麼時候初始化以及匯入,這裡要注意類的初始化方法 以及是不是有可能多執行緒的問題以及有依賴
2 匯入程式集的方式可以直接匯入程式集或者通過檔案,看了反編譯的程式碼以及.netcore的原始碼,底層是使用load 以及loadfrom的方法來時間載入程式集的,所以這玩意理論上應該實現不了熱插拔把·
3 關於.net實現熱插拔,網上有很多玩法,之前有看過通過appdomain 來實現,也就是應用程式域,實現略複雜這裡沒研究,也可以通過load的方式重新載入程式集·但是這些理論上應該做不到所謂的熱插拔吧,起碼程式要重啟把···
4 之前有面試問MEF 怎麼實現熱插拔,直接懵逼了,我是搞清楚。後來想了下,可以換一個方式實現,在MEF基礎上實現AOP,通過aop實現許可權控制,攔截某些操作,或者MEF 載入的時候過濾載入項,這些算熱插拔麼···