以前面試有問道依賴注入,由於一直是作客戶端的發開發,沒有接觸這個,後邊工做接觸到了MEF,順便熟悉一下依賴注入git
詳細的概念解釋就不講了,網上一大把,我的覺着依賴注入本質是爲了解耦,方便擴展面試
依賴注入的方式:屬性注入和構造函數注入,還有接口注入的,看了下跟屬性注入差很少·就不展現了多線程
上代碼:app
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框架就是幹這個的, 本質是經過特性和反射在運行的時候程序集動態加載。dom
//接口聲明 //最終調用過程接口 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會自動導入導出的this
這裏是導出,下邊看怎麼導入使用spa
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目錄下便可.net
實現了一個類:
[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 加載的時候過濾加載項,這些算熱插拔麼···