以前面试有问道依赖注入,由于一直是作客户端的发开发,没有接触这个,后边工做接触到了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 加载的时候过滤加载项,这些算热插拔么···