Unity IOC

Unity IOC的一些理解

 1.什么是IOC?

IOC(Inversion of Control),控制反转,又称为“依赖注入(DI =Dependence Injection)

一句话描述:把创建对象的权力交给第三方控制。不再直接使用new去创建对象,而是通过第三方容器去创建,管理,在使用对象实例时从第三方容器去获取。

2.为什么需要IOC?

解耦。使程序依赖于抽象,不依赖于具体实现。

3.RegisterType和Resolve的区别

看一个最简单的例子:

IPerson接口:

public interface IPerson
    {
        string Name { get; set; }
        void work();
    }

Student类:

public class Student : IPerson
    {
        public string Name { get  ; set  ; }

        public void work()
        {
            Console.WriteLine("Student work");
        }
    }

Main:

private static void Main(string[] args)
    {
        //1.实例化IOC容器
        UnityContainer unityContainer = new UnityContainer();
        //2.注册抽象和具体
        unityContainer.RegisterType<IPerson, Student>( );
        //3.从IOC容器中获取该类型的实例
        IPerson student = unityContainer.Resolve<IPerson>( );
        student.work();
    }

  RegisterType是注册一种关联,并不是创建对象,也可以理解为Mapping,一种映射关系。

unityContainer.RegisterType<IPerson, Student>( );

这句是把IPerson和Student关联起来。IPerson是接口,Student继承了IPerson接口。当程序在使用IPerson对象实例时,IOC容器自动帮我们创建一个Student类型的实例。

IPerson student = unityContainer.Resolve<IPerson>( );

这一句相当于 IPerson student = new Student();只是把new对象实例的这一步交给了IOC容器去完成,这体现出控制反转的含义。

4.代码例子

 看一些细节:

4.1.RegisterType不指定name,则后创建的会覆盖先创建的

     UnityContainer unityContainer = new UnityContainer();
        //2.注册抽象和具体
        unityContainer.RegisterType<IPerson, Student>( );
        //3.从IOC容器中获取该类型的实例
        IPerson student1 = unityContainer.Resolve<IPerson>( );
        IPerson student2 = unityContainer.Resolve<IPerson>( );

        bool ret = object.ReferenceEquals(student1,student2);

第3步,从IOC容器获取的实例student1和student2不是同一个,只会存在一个Student实例

 4.2 RegisterType指定name,可以获取到多个不同的实例

UnityContainer unityContainer = new UnityContainer();
            //2.注册抽象和具体
            unityContainer.RegisterType<IPerson, Student>("xiaowang");
            unityContainer.RegisterType<IPerson, Student>("zhangsan");
            //3.从IOC容器中获取该类型的实例
            IPerson student1 = unityContainer.Resolve<IPerson>("xiaowang");
            student1.Name = "xiaowang";
            IPerson student2 = unityContainer.Resolve<IPerson>("zhangsan");
            student2.Name = "zhangsan";

IOC容器中有2个Student的实例

 

在从IOC容器获取Student实例时,也需要加上注册时写的name,不然会出错。

 4.3IOC创建对象实例时怎么选构造函数?

Student类包含2个构造函数,IOC容器在创建Student实例时,调用哪个构造函数?

public class Student : IPerson
    {
        public string? Name { get; set; }

        public void work()
        {
            Console.WriteLine("Student work");
        }
        //无参构造函数
        public Student()
        {
            Console.WriteLine("Student");
        }
        //一个参数的构造函数
        public Student(int number)
        {
            Console.WriteLine("Student(string str)="+ number);
        }
    }
      //1.实例化IOC容器
            UnityContainer unityContainer = new UnityContainer();
            //2.注册抽象和具体
            unityContainer.RegisterType<IPerson, Student>();
            //3.从IOC容器中获取该类型的实例
            IPerson student1 = unityContainer.Resolve<Student>();

调用了无参构造函数。

如果我想让IOC在创建Student实例时调用带一个参数的构造函数怎么处理?

 只需要在需要被调用的构造函数前加一个特性即可[InjectionConstructor],又多一个问题,这个int number参数从哪里来?既然是IOC容器负责创建Student对象,那么这个参数也由IOC容器提供,

所以我们需要注册一个

 4.4 构造函数注入

定义2个接口IStudent和ITeacher

public interface IStudent
    {
        public int id { get; set; }
        void study();
    }
public interface ITeacher
    {
        int id { get; set; }
        void Teach();
    }
public class Student : IStudent
    {
        public int id { get ; set; }
public void study()
        {
             
        }

        //无参构造函数
        public Student( ITeacher teacher)
        {
            Console.WriteLine("Student 构造函数");
        }
    }
public class Teacher : ITeacher
    {
        public int id { get; set; }

        public void Teach()
        {
            
        }
        public Teacher()
        {
            Console.WriteLine("Teacher 构造函数");
        }   
    }

 Student类中的构造函数需要一个ITeacher 类型的参数,则在IOC构建Student实例时也会自动创建ITeacher的实例(需要提前给ITeacher注册registerType)      

       //1.实例化IOC容器
       UnityContainer unityContainer = new UnityContainer(); //2.注册抽象和具体 unityContainer.RegisterType<IStudent, Student>(); unityContainer.RegisterType<ITeacher, Teacher>(); //3.从IOC容器中获取该类型的实例 IStudent student1 = unityContainer.Resolve<IStudent>();

什么是构造函数注入呢?

通常只要我们使用了RegisterType注册了类型,则通过Resolve可以获取类型的实例,但是当我们不直接使用Resolve时,而是在定义A类时,会调用A的构造函数,这时候发现,A的构造函数需要另B的实例,则IOC会自动创建出B实例,再传给A类。

4.5 构造函数注入的顺序

Studen的构造函数需要Iteacher,则Student依赖于ITeacher。

总结:A依赖于B,B依赖于C,则先创建C,C创建完成后,把C传给B。B创建完成后,把B传给A,最后创建A。这个过程是由IOC容器自动完成的。

4.6 属性注入

除了在构造函数中注入,还有属性注入的方法,也可以自动完成这一系列操作。

如把上面的例子改成如下:

 特别注意的是属性注入的属性的访问权限是public的,private的不行。

4.7方法注入

 构造函数在创建对象是自动调用,所以构造函数注入也是自动的。如果我们有一个方法,这个方法的参数也需要ITeacher,这时候需要用到方法注入了。

test方法的访问权限也必须是public的。

4.8生命周期问题

 TransientLifetimeManager 瞬时生命周期:每一次获取都是全新的实例

ContainerControlledLifetimeManager 单例生命周期:同一个进程中,同一个容器,获取的都是同一个实例

PerThreadLifetimeManager 线程单例生命周期:在同一个线程中,同一个容器获取得到的实例是同一个实例

默认是(什么都不指定的情况下)TransientLifetimeManager,上面的例子都是这种。

ContainerControlledLifetimeManager例子

 

 PerThreadLifetimeManager 例子

 

参考:

unity ioc quickstart

Using Unity in Applications | Unity Container

posted @ 2022-08-24 22:23  薛定谔的小灯泡  阅读(755)  评论(1编辑  收藏  举报