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