• C# 依赖注入 & MEF


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

      

  • 相关阅读:
    26 playbooke(二)
    25 playbook(一)
    23 ansible模块2
    23 ansible模块(一)
    22 Ansible相关工具、ansible、ansible-vault、ansible-console、ansible-galaxy
    21 Ansible安装与主机清单的编写
    Druid.jar包
    Servlet--request
    mysql-myini配置
    global,tornado的write_error,tornado的中间件的写法
  • 原文地址:https://www.cnblogs.com/onegarden/p/10383056.html
Copyright © 2020-2023  润新知