打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
依赖注入框架Autofac(IOC)的简单使用

Autofac和其他容器的不同之处是它和C#语言的结合非常紧密,在使用过程中对你的应用的侵入性几乎为零,更容易与第三方的组件集成,并且开源,Autofac的主要特性如下:

1,灵活的组件实例化:Autofac支持自动装配,给定的组件类型Autofac自动选择使用构造函数注入或者属性注入,Autofac还可以基于lambda表达式创建实例,这使得容器非常灵活,很容易和其他的组件集成。
2,资源管理的可视性:基于依赖注入容器构建的应用程序的动态性,意味着什么时候应该处理那些资源有点困难。Autofac通过容器来跟踪组件的资源管理。对于不需要清理的对象,例如Console.Out,我们调用ExternallyOwned()方法告诉容器不用清理。细粒度的组件生命周期管理:应用程序中通常可以存在一个应用程序范围的容器实例,在应用程序中还存在大量的一个请求的范围的对象,例如一个HTTP请求,一个IIS工作者线程或者用户的会话结束时结束。通过嵌套的容器实例和对象的作用域使得资源的可视化。
3,Autofac的设计上非常务实,这方面更多是为我们这些容器的使用者考虑:
●组件侵入性为零:组件不需要去引用Autofac。
●灵活的模块化系统:通过模块化组织你的程序,应用程序不用纠缠于复杂的XML配置系统或者是配置参数。
●自动装配:可以是用lambda表达式注册你的组件,autofac会根据需要选择构造函数或者属性注入
●XML配置文件的支持:XML配置文件过度使用时很丑陋,但是在发布的时候通常非常有用

Autofac的简单使用,并加入了Repository模式.

定义两个简单实体类:public class Persion{    public string Name { get; set; }    public int Age { get; set; }}public class Custom{    public string CustomName { get; set; }    public int CustomID { get; set; }}


定义泛型数据库访问接口:public interface Idal<T> where T:class{    void Insert(T entity);    void Update(T entity);    void Delete(T entity);}


泛型数据库访问接口的泛型实现:public class Dal<T>:Idal<T> where T : class{    #region Idal<T> Members    public void Insert(T entity)    {        HttpContext.Current.Response.Write("您添加了一个:"           +entity.GetType().FullName);    }    public void Update(T entity)    {        HttpContext.Current.Response.Write("您更新一个:"             +entity.GetType().FullName);    }    public void Delete(T entity)    {        HttpContext.Current.Response.Write("您删除了一个:"              +entity.GetType().FullName);    }    #endregion}


使用Repository模式实现访问。

Repository的泛型接口:public interface IRepository<T> where T:class{    void Insert(T entity);    void Update(T entity);    void Delete(T entity);}


Repository泛型接口的泛型实现:public class Repository<T>:IRepository<T> where T:class{    private Idal<T> _dal;    public Repository(Idal<T> dal)    {        _dal = dal;    }    #region IRepository<T> Members    public void Insert(T entity)    {        _dal.Insert(entity);    }    public void Update(T entity)    {        _dal.Update(entity);    }    public void Delete(T entity)    {        _dal.Delete(entity);    }    #endregion}


IDependency的依赖接口,不需要任何方法体,所有的业务对象都实现该接口public interface IDependency{}


实现IDependency接口的CustomBll类,通过Repository模式存储数据。public class CustomBll:IDependency{    private readonly IRepository<Custom> _repository;    public CustomBll(IRepository<Custom> repository)    {        _repository = repository;    }    public void Insert(Custom c)    {        _repository.Insert(c);    }    public void Update(Custom c)    {        _repository.Update(c);    }    public void Delete(Custom c)    {        _repository.Delete(c);    }}


实现IDependency接口的PersionBll类,通过Repository模式存储数据。public class PersionBll:IDependency{    private readonly IRepository<Persion> _repository;    public PersionBll(IRepository<Persion> repository)    {        _repository = repository;    }    public void Insert(Persion p)    {        _repository.Insert(p);    }    public void Update(Persion p)    {        _repository.Update(p);    }    public void Delete(Persion p)    {        _repository.Delete(p);    }}


下面编写组件实例化测试

var builder = new ContainerBuilder();builder.RegisterGeneric(typeof(Dal<>)).As(typeof(Idal<>))    .InstancePerDependency();builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>))    .InstancePerDependency();builder.Register(c=>new PersionBll((IRepository<Persion>)    c.Resolve(typeof(IRepository<Persion>))));builder.Register(c => new CustomBll((IRepository<Custom>)    c.Resolve(typeof(IRepository<Custom>))));//var container = builder.Build()教程里都是使用这行代码,//我本地测试需要加入ContainerBuildOptions枚举选项。using (var container = builder.Build(ContainerBuildOptions.None))  {    // var repository= container.Resolve(typeof(IRepository<Persion>),new TypedParameter());    // IRepository<Persion> _repository = repository as Repository<Persion>;    // var m = new PersionBll(_repository);    Persion p = new Persion();    p.Name = "小人";    p.Age = 27;    var m = container.Resolve<PersionBll>();    m.Insert(p);    Custom c = new Custom();    c.CustomName = "小小";    c.CustomID = 10;    var cc = container.Resolve<CustomBll>();    cc.Update(c);}


这里通过ContainerBuilder方法RegisterGeneric对泛型类进行注册(当然也可以通过ContainerBuilder方法RegisterType对不是泛型的类进行注册),当注册的类型在相应得到的容器中可以Resolve你的类实例。
builder.RegisterGeneric(typeof(Dal<>)).As(typeof(Idal<>)).InstancePerDependency();通过AS可以让类中通过构造函数依赖注入类型相应的接口。(当然也可以使用builder.RegisterType<类>().As<接口>();来注册不是泛型的类 )
Build()方法生成一个对应的Container实例,这样,就可以通过Resolve解析到注册的类型实例。

注:如果要获得某个泛型的实例,需要将泛型T代表的类传进去。如上c.Resolve(typeof(IRepository<Persion>))返回的是Object,需要转换为响应的接口。

当然可以使用autofac的新特性RegisterAssemblyTypes,从一个程序集的注册类型设置根据用户指定的规则,例子如下:

var builder = new ContainerBuilder();builder.RegisterGeneric(typeof(Dal<>)).As(typeof(Idal<>)).InstancePerDependency();builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerDependency();//上面的那些类如果在单独的工程里,如生成的程序集为AutofacUnitTest,就可以使用//Assembly.Load("AutofacUnitTest")获得响应的程序集。如果所有的文件在一个控制台程序里,//可以通过Assembly.GetExecutingAssembly(); 直接获得相应的程序集。Assembly dataAccess = Assembly.Load("AutofacUnitTest"); builder.RegisterAssemblyTypes(dataAccess)        .Where(t => typeof(IDependency).IsAssignableFrom(t) && t.Name.EndsWith("Bll"));//RegisterAssemblyTypes方法将实现IDependency接口并已Bll结尾的类都注册了,语法非常的简单。


IocInverse of control)已经是叫嚷了很久的技术了,一直没有机会细看,最近因为看源代码的关系,研究了一点,拿出来分享一下。

当前网络上有很多Ioc的框架,比如说微软的企业库就使用Ioc技术重写了,还有Prism模式也用到了Ioc。我看的函数库是Autofac,但是理念跟其他的函数库大同小异,实际上,为了方便程序员在不同的Ioc框架上移植程序,各个框架的编写者开会定义了一个大家都支持的接口集:Common Service Locator

什么是Ioc

Ioc简言之,就是将类似下面创建对象的代码—我们称之为情况1

var checker = new MemoChecker(memos, new PrintingNotifier(Console.Out));

转换成下面这样—称之为情况2

var checker = container.Resolve<MemoCheck>();

container.Resolve<MemoCheck>这一行代码在创建MemoCheck这个类型的实例时,又可以通过下面的代码创建MemoCheck构造函数所需要的两个参数:

new MemoChecker(container.Resolve<IQueryable<Memo>>(),
                container.Resolve
<IMemoDueNotifier>())


情况2相对情况1的好处在于,在情况1 的代码里,程序员需要显式指定构建MemoChecker实例所要求的参数类型的实例。也就是说,MemoChecker在构造一个实例时,你需要显式传入第二个参数的具体实例(PrintingNotifier)。这样就导致一个问题,如果在后期程序发布以后,需要更换MemoCheck的第二个参数,那就只有修改程序代码一条路可走了。

针对于情况1的这个问题,那肯定有人会说,那就把MemoChecker构造函数的第二个参数定义成一个接口,然后在创建MemoChecker实例的时候,读一个配置文件,找到实现这个接口的具体类型,通过反射等机制创建对象传给MemoChecker的构造函数。这样就可以通过修改配置文件的方式,通过添加实现接口的插件,动态地修改程序的行为—这正是情况2所要做的,也就是Ioc和依赖注入(Dependence Injection)要解决的一个通用问题。

关于Ioc和依赖注入,网上已经有很多文章讲解这个概念了,有兴趣的朋友可以看看这篇文章,里面介绍的很详细:

http://martinfowler.com/articles/injection.html

使用Autofac实现依赖注入

我先以CodeProject的一个示例代码为例,讲解一下用Autofac实现依赖注入的基本步骤,下面是代码:


 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.IO; 
 5 using Autofac;
 6 
 7 namespace Remember
 8 {
 9     interface IMemoDueNotifier
10     {
11         void MemoIsDue(Memo memo);
12 }
13 
14     class Memo
15     {
16         public string Title { getset; }
17         public DateTime DueAt { getset; }
18 
19 
20     class MemoChecker
21     {
22         readonly IList<Memo> _memos;
23         readonly IMemoDueNotifier _notifier;
24 
25         public MemoChecker(IList<Memo> memos, IMemoDueNotifier notifier)
26         {
27             _memos = memos;
28             _notifier = notifier;
29         }
30 
31         public void CheckNow()
32         {
33             var overdueMemos = _memos.Where(memo => memo.DueAt < DateTime.Now);
34 
35             foreach (var memo in overdueMemos)
36                 _notifier.MemoIsDue(memo);
37         }
38     }
39 
40     class PrintingNotifier : IMemoDueNotifier
41     {
42         readonly TextWriter _writer;
43 
44         public PrintingNotifier(TextWriter writer)
45         {
46             _writer = writer;
47         }
48 
49         public void MemoIsDue(Memo memo)
50         {
51             _writer.WriteLine("Memo '{0}' is due!", memo.Title);
52         }
53 }
54 
55     class Program
56     {
57         static void Main()
58         {
59             var memos = new List<Memo> {
60                 new Memo { Title = "Release Autofac 1.1"
61                            DueAt = new DateTime(20070312) },
62                 new Memo { Title = "Update CodeProject Article"
63                            DueAt = DateTime.Now },
64                 new Memo { Title = "Release Autofac 3"
65                            DueAt = new DateTime(20110701) }
66             };
67 
68             var builder = new ContainerBuilder();
69             builder.Register(c => new MemoChecker(
70                 c.Resolve<IList<Memo>>(), c.Resolve<IMemoDueNotifier>()));
71             builder.RegisterType<PrintingNotifier>().As<IMemoDueNotifier>();
72             builder.RegisterInstance(memos).As<IList<Memo>>();
73                
74             builder.RegisterInstance(Console.Out)
75                    .As<TextWriter>()
76                    .ExternallyOwned();
77 
78             using (var container = builder.Build())
79             {
80                 container.Resolve<MemoChecker>().CheckNow();
81             }
82 
83             Console.WriteLine("Done! Press any key.");
84             Console.ReadKey();
85         }
86     }
87 }
88 


这个程序的作用是检查所有的记事项,提醒用户这些过期的记事项。这个程序里最主要的类是MemoCheckerMemoChecker需要两个对象才能构建一个实例—MemoIMemoDueNotifier。而这两个类型的对象,是由autofac自行解析的,autofac知道如何找到一个接口是由哪个对象实现的—这个过程叫做Resolve。而接口和实现接口对象的对映关系是由程序员在配置文件app.config,或者自己在程序的入口处(例如Main函数)注册好的—这个过程叫Register。因为实现接口的某些对象,有可能它的构造函数也会接受其他接口,而实现这些接口的对象也需要解析。因此,Autofac将所有的接口,和实现接口的对象都放到一个容器里,这个容器自己解析实现接口的对象之间的依赖关系—也就是ContainerBuilderContainerBuilderBuild的过程中,通过多次调用Resolve解决容器内部的对象依赖关系。当依赖关系都解析完毕以后,以后要创建对象,不需要再用类似下面的代码显式创建了:

var builder = new MemoChecker();


创建对象的工作,全部都交给Container解决,Container自己在内部找到构造对象时,Container创建调用构造函数要用到的参数的对象,解决对象之间的依赖关系,然后你只要用类似下面的代码就可以获取到你要的对象:

var builder = container.Resolve<MemoChecker>();

使用Autofac基于配置文件实现依赖注入

前面讲到的依赖注入,还是基于代码的,很多时候,使用Ioc和依赖注入技术,主要是为了支持插件技术。比如说,其他插件只要实现了定义的接口,那么,终端用户理论上可以只通过将实现插件的assembly拷贝到程序文件夹,并修改配置文件的形式来无缝集成新的插件。

那我们来看Autofac自带的例子—Calculator。这个程序有三个Assembly组成,Calculator是那个支持插件的程序;Calculator.Api包括了接口的定义,这样,Calculator和它的插件通过引用这个Assembly,就可以实现相互交互了;而Calculator.Operations就是最后实现接口的一些插件。

我们来看一看代码:

Calculator.Api定义了一个接口—这个接口将会被Calculator(支持插件的程序)和Calculator.Operations(插件)所使用:


 1 using System;
 2 
 3 namespace Calculator.Api
 4 {
 5     public interface IOperation
 6     {
 7         string Operator
 8         {
 9             get;
10         }
11 
12         double Apply(double lhs, double rhs);
13     }
14 }
15 


而在Calculator这个Assembly里,定义了一个Calculator这个类,枚举所有实现了IOperation的插件—这个枚举过程由Autofac自动完成:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using Calculator.Api;
 6 
 7 namespace Calculator
 8 {
 9     class Calculator
10     {
11         IDictionary<string, IOperation> _operations = new Dictionary<string, IOperation>();
12 
13         public Calculator(IEnumerable<IOperation> operations)
14         {
15             if (operations == null)
16                 throw new ArgumentNullException("operations");
17 
18             foreach (IOperation op in operations)
19                 _operations.Add(op.Operator, op);
20         }
21 
22         public IEnumerable<string> AvailableOperators
23         {
24             get
25             {
26                 return _operations.Keys;
27             }
28         }
29 
30         public double ApplyOperator(string op, double lhs, double rhs)
31         {
32             if (op == null)
33                 throw new ArgumentNullException("op");
34 
35             IOperation operation;
36             if (!_operations.TryGetValue(op, out operation))
37                 throw new ArgumentException("Unsupported operation.");
38 
39             return operation.Apply(lhs, rhs);
40         }
41     }
42 }
43 


请注意Calculator的构造函数,这个构造函数接受一个IEnumerable<IOperation>类型的参数,这个参数是autofac通过读取配置文件自动构建好一个实例,下面就是app.config文件里的具体设置:

 1 <?xml version="1.0"?>
 2 <configuration>
 3   <configSections>
 4     <section name="calculator" type="Autofac.Configuration.SectionHandler, Autofac.Configuration"/>
 5   </configSections>
 6 
 7   <calculator defaultAssembly="Calculator.Api">
 8     <components>
 9       <component type="Calculator.Operations.Add, Calculator.Operations" member-of="operations"/>
10       <component type="Calculator.Operations.Multiply, Calculator.Operations" member-of="operations"/>
11 
12       <component type="Calculator.Operations.Divide, Calculator.Operations" member-of="operations">
13         <parameters>
14           <parameter name="places" value="4"/>
15         </parameters>
16       </component>
17 
18     </components>
19   </calculator>
20 
21 </configuration>
22 


 

在程序(Calculator)启动的时候,调用Autofac API里面的ContainerBuilder.RegisterModule来告诉Autofac读取配置文件里的接口与实现接口对象的映射关系。


 1 namespace Calculator
 2 {
 3 
 4     static class Program
 5     {
 6         [STAThread]
 7         static void Main()
 8         {
 9             try
10             {
11                 var builder = new ContainerBuilder();
12 
13                 ...
14 
15                 builder.RegisterModule(new ConfigurationSettingsReader("calculator"));
16 
17                 ...
18             }
19             catch (Exception ex)
20             {
21                 DisplayException(ex);
22             }
23         }
24     }
25 }
26 



上节说了一下基本的理论知识,例子可能不太好,不过无所谓了,目的是要让大家明白啥是依赖倒置和依赖注入,目的就达到了,简单一句话,这2玩意都是用来解耦合的。

不过依赖倒置这个词哥哥真不敢苟同,哥哥来个颠覆的说法,我说这是依赖正置。

因为本来就应该是上层依赖上层嘛,低层也应该依赖上层,但是由于程序语言的原因,导致代码和实际完全不符合,搞得抽象经常依赖具体,具体更是依赖具体。

体现在代码中就是接口中关联类型,类型中也关联类型。完全反了。所以我们呢要让他正常起来,让接口只依赖接口,类也只依赖接口,这个实际比较相符合,所以哥哥我叫他依赖正置。

说了依赖正置,接下来再说说控制反转IOC,我们要说的autofac就是是IOC的一个框架。

啥是控制反转吧?你想啊,我们一般写代码是这样:举个去死的栗子

public interface IPerson{  void GoToHell(IPlace hell);}public class CodeFarmer : IPerson{  public void GoToHell(IPlace hell)  {    hell.Accept(this);  }}public interface IPlace{  void Accept(IPerson person);}public class Hell : IPlace{  public void Accept(IPerson person)  {    // do sth..  }}public class God{  public void Do()  {    IPerson you = new CodeFarmer();    you.GoToHell(new Hell());  }}

看,咱们是没有违反基本的DIP原则吧,基本都是依据接口编程的。

但是还是老话,在客户端god哪里还是有问题,他要你去死,还必须要知道hell的创建逻辑,god表示你这东西简直不能用,我只是要你去死而已呀,我还要给你指明通向hell的道路?

god表示很忙!把hell的初始化放在person 中如何?可以,不是有句话说,人一出生,就是坐上了通往死亡的列车,谁说的?好像是我自己!!

public interface IPerson{  IPlace Hell { get; set; }  void GoToHell();}public class CodeFarmer : IPerson{  public IPlace Hell { get; set; }  public CodeFarmer()  {    Hell = new Hell();  }  public void GoToHell()  {    Hell.Accept(this);  }}public interface IPlace{  void Accept(IPerson person);}public class Hell : IPlace{  public void Accept(IPerson person)  {    // do sth..  }}public class God{  public void Do()  {    IPerson you = new CodeFarmer();    you.GoToHell();  }}

这样,你自己往hell走就行了,god表示我是老板,我只发指令给你,具体的路线你自己去搞!
但是CodeFarmer就不忙?最后要死了都要自己找路去死?简直不能干,这个行业!那怎么办?这个new Hell()的部分放在那里好呢?

其实这个就是我们程序里面常见的问题,依赖的具体对象创建到哪里注入比较好呢?好像哪里都不符合逻辑!

那既然这样,那创世者说我发个公告牌如何?我就告诉你,这个地方有个公告板,里面告诉你了地狱怎么走,你要去的话,你自己去看看就得了,没必要自己摸路。

代码如下:

public interface IPerson{  void GoToHell(IPlace hell);}public class CodeFarmer : IPerson{  public void GoToHell(IPlace hell)  {    hell.Accept(this);  }}public interface IPlace{  void Accept(IPerson person);}public class Hell : IPlace{  public void Accept(IPerson person)  {    // do sth..  }}public class God{  public void Do()  {    IPerson you = InfoBoard.PlaceInfo["person"];    you.GoToHell(InfoBoard.PlaceInfo["hell"]);  }}public class InfoBoard{  public static Dictionary<string, IPlace> PlaceInfo = new Dictionary<string, IPlace>();  static InfoBoard()  {     PlaceInfo.Add("hell", new Hell());PlaceInfo.Add("person", new CodeFarmer());  }}

这样,不管是在god中,还是在codefamer中,直接可以使用InfoBoard.PlaceInfo["hell"]来去到通往地狱的路,有指明灯多好啊。

比较官方的语言:应用控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用,传递给它。也可以说,依赖被注入到对象中。所以,控制反转是,关于一个对象如何获取他所依赖的对象的引用,这个责任的反转。

怎么样,真jb难懂是吧?

我就给你翻译成人类能懂的话,你不是要面向接口编程吗?你接口,你TM最后总要有new的时候吧?你一旦new了,一个对象和另一个对象实际上的强依赖是不是产生了?既然这样,那我们都不new,由一个在程序看来虚无缥缈的地方保存有所有抽象类和接口对象的具体实现。从哪里取就行了,这样可好?那我和你永远只关心接口就行了

就像上面的栗子中,不管是god中还是codefarmer还是hell中,是不是没有任何创建彼此的代码?是不是这个耦合彻底解除了(或者说是转移了到InfoBoard中了,便于集中管理,前文有提到)?

我告诉你,这个就是一个最简单的ioc容器。控制反转,我还是那句话,我自己给他定义一个名词,控制转移比较好!谈不上什么正反,这个!!

那有的同学问,你TM这用你昨天讲的反射不是可以达到一样的效果?你说你玩这个,你玩他有什么用啊?我想说的是,就是可以达到一样的效果!!但是,我只多说一个词,性能!其他的我不多讲。如果你的系统没那么多jjyy的性能的事情,完全用反射就行了

那IOC容器就这样就行了?肯定不行啊,举个栗子:
我们用ado访问数据库

public class InfoBoard{  public static Dictionary<string, object> PlaceInfo = new Dictionary<string, object>();  static InfoBoard()  {    PlaceInfo.Add("conn", new SqlConnection("connect string"));    PlaceInfo.Add("comm", new SqlCommand("SELECT *****************", InfoBoard.PlaceInfo["conn"] as SqlConnection));  }}

看如果我执行一次comm后实际上SqlConnection如果释放了,这个comm是不是没用了?

如果要保证comm有效这个SqlConnection是不是不能释放,这2种方式都不是我们想要的,所以,你必须要有个对象过期策略,和非托管资源的释放问题。

这个SqlConnection根被就不能写在静态构造中,实际的ioc框架还会遇到很多问题,这就是一个autofac代码茫茫多的原因

西门子哪个广告语,我一直拿着用,精于心,简于形,为了一点点的性能问题,背后的工作是巨大的,还好有现成的框架帮我们做了很多事情。

下面说下Autofac

下载,引用,或者通过NUGET方式获取框架,这些不多说。正常人都能搞定

using Autofac;public interface IPerson{  IPlace Hell { get; set; }  string Name { get; set; }  void GoToHell();}public class CodeFarmer : IPerson{  public string Name { get; set; }  public IPlace Hell { get; set; }  public CodeFarmer(IPlace Hell)  {    this.Hell = Hell;  }  public void GoToHell()  {    Hell.Accept(this);  }}public interface IPlace{  void Accept(IPerson person);}public class Hell : IPlace{  public void Accept(IPerson person)  {    Console.WriteLine(person.Name+" is gonna die");    Console.ReadKey();  }}class Program{  private static IContainer Container { get; set; }  static void InitApplication()  {    var builder = new ContainerBuilder();    builder.RegisterType<Hell>().As<IPlace>();    builder.RegisterType<CodeFarmer>().As<IPerson>();    Container = builder.Build();  }  static void Main(string[] args)  {    InitApplication();    var aSadMan = Container.Resolve<IPerson>();    aSadMan.Name = "hard worker";    //var goodPlace = Container.Resolve<IPlace>();    aSadMan.GoToHell();  }}

主要看Program

private static IContainer Container { get; set; }

有个Container容器有没有?
builder.RegisterType<Hell>().As<IPlace>();
builder.RegisterType<CodeFarmer>().As<IPerson>();
往容器里面加东西有没有?
var aSadMan = Container.Resolve<IPerson>();
从容器里面取东西有没有?

那有的人就问了,你CodeFarmer里面的IPlace对应的hell是怎么实例化的?CodeFarmer的构造函数都没看到调用啊?
这里就是所谓的构造函数注入,你只需要知道,这样写,然后解析他的上层类IPerson,这个hell是自动实例化的,够造函数自动被调用了,里面的参数自动被解析成hell,因为你前面有往container中registertype过,这样就行了,是不是很强大呢?

public IPlace Hell { get; set; }  public CodeFarmer(IPlace Hell)  {    this.Hell = Hell;  }

如果Hell的构造里面还要注入其他的依赖,这个解析可以一直嵌套下去,无论有多少层,只要你从最上面的入口做了类似

 var aSadMan = Container.Resolve<IPerson>();

的操作!

行了,先说到这,下回再扯吧,欢迎拍砖,往死里拍,上文有个错误,把依赖倒置说成了DI应该是dip,汗!因为,我写这些,例子都是我自己随便想的,基本上时间也仓促,难免有不完全对的地方,但是核心我都说明白了的,大家有问题可以提出来探讨,就是不要直接说有问题,但是不说明问题再那里,这样就没意思了!基本我都不会检查第2遍。人都说第一想到的东西都是最真实和正确的,有没有?

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Autofac入门
C#接口
NET Core 3.0 项目中使用 AutoFac
ASP.NET Core 2.2 WebApi 系列【三】AutoFac 仓储接口的依赖注入
通过配置的方式使用Autofac
c#中的interface abstract与virtual
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服