控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
IoC(控制反转): 就是将原先的new对象这个操作交由第三方容器,由容器统一创建对象并管理其创建对象的生命周期;
DI(依赖注入): 我理解其中“依赖”有两层意思:
- 类与类之间的依赖关系;
- 对象的创建依赖于容器;
"注入":不用主动从容器中获取对象,由容器根据对象依赖关系自动注入;
依赖注入:程序将对象控制权交给容器,统一依赖容器创建对象,类之间的依赖,也是通过容器自动注入;(注:注入的前提是要把对象的控制权交给容器)
依赖注入优点:
- 轻松管理类及类之间的依赖;
- 减少代码耦合性;
- 提高代码维护性和可扩展性;
- 由容器统一创建对象和管理生命周期;
一、Unity 容器
1.安装Unity依赖包
install-package unity
2.使用unity实现DI
定义一个接口类 ILanguage
public interface ILanguage
{
public string GetContent();
}
定义两个实现类 Chinese,English
public class Chinese : ILanguage
{
public string GetContent()
{
return "我爱学习";
}
}
public class English : ILanguage
{
public string GetContent()
{
return "I Love Learning";
}
}
在main函数中使用unity进行依赖管理,实现依赖注入
static void Main(string[] args)
{
// 容器注册
var container = new UnityContainer();
// 依赖注入
container.RegisterType<ILanguage, Chinese>();
// 获取依赖实例
var language = container.Resolve<ILanguage>();
//输出内容
Console.WriteLine(learn.GetContent());
}
执行结果
image.png一个接口实现多个注册
static void Main(string[] args)
{
// 容器注册
var container = new UnityContainer();
// 依赖注入
container.RegisterType<ILanguage, Chinese>();
//通过不同名称进行同个接口多个注册
container.RegisterType<ILanguage, English>("english");
// 获取依赖实例
var chinese = container.Resolve<ILanguage>();
var english = container.Resolve<ILanguage>("english");
//输出内容
Console.WriteLine(chinese.GetContent());
Console.WriteLine(english.GetContent());
}
执行结果
image.png对象的生命周期
- Transient (瞬时):默认类型,每次使用都会创建新的实例
- Singleton(单例):整个根容器的生命周期内是同一个对象,只会实例化一次
- PerThread (线程作用域):在同一个线程里单例,只会被实例化一次
static void Main(string[] args)
{
// 容器注册
var container = new UnityContainer();
//依赖注入 瞬时默认
container.RegisterType<ILanguage, Chinese>();
var language1 = container.Resolve<ILanguage>();
var language2 = container.Resolve<ILanguage>();
Console.WriteLine($"-----------------------瞬时默认-------------------------");
Console.WriteLine($"language1 与 language2:{ReferenceEquals(language1,language2)}\r\n");
//线程单例
Console.WriteLine($"-----------------------线程单例-------------------------");
container.RegisterType<ILanguage, Chinese>(TypeLifetime.PerThread);
ILanguage tlanguage1 = null;
var task1 = Task.Run(() => {
tlanguage1 = container.Resolve<ILanguage>();
Console.WriteLine($"tlanguage1 线程id:{Thread.CurrentThread.ManagedThreadId}");
});
ILanguage tlanguage2 = null;
ILanguage tlanguage3 = null;
var task2 = Task.Run(() => {
tlanguage2 = container.Resolve<ILanguage>();
tlanguage3 = container.Resolve<ILanguage>();
Console.WriteLine($"tlanguage2 线程id: {Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine($"tlanguage3 线程id:{Thread.CurrentThread.ManagedThreadId}");
});
task1.Wait();
task2.Wait();
Console.WriteLine($"tlanguage1 与 tlanguage2 :{ReferenceEquals(tlanguage1, tlanguage2)}");
Console.WriteLine($"tlanguage2 与 tlanguage3 :{ReferenceEquals(tlanguage2, tlanguage3)}\r\n");
//单例模式
container.RegisterType<ILanguage, Chinese>(TypeLifetime.Singleton);
var slanguage1 = container.Resolve<ILanguage>();
var slanguage2 = container.Resolve<ILanguage>();
Console.WriteLine($"-----------------------单例模式-------------------------");
Console.WriteLine($"slanguage1 与 slanguage2:{ReferenceEquals(language1, language2)}");
}
执行结果:
image.png二、Autofac容器
AutoFac和Unity容器的用法步骤类似
创建容器 > 注册接口实现类 > 创建接口容器 > 创建实例 > 调用实例方法
安装依赖包
install-package autofac
main函数使用autofac管理依赖
static void Main(string[] args){
//创建容器
ContainerBuilder builder = new ContainerBuilder();
//依赖注入
builder.RegisterType<Chinese>().As<ILanguage>();
//创建接口容器
IContainer container = builder.Build();
var language = container.Resolve<ILanguage>();
//输出内容
Console.WriteLine(language.GetContent());
}
输出结果:
程序集加载
注册实现类多了只会注册比较麻烦,可以使用注册程序集统一注册
static void Main(string[] args){
//创建容器
ContainerBuilder builder = new ContainerBuilder();
//程序集加载
Assembly assembly = Assembly.Load("IOC");// IOC程序集名称
//依赖注入 把所有接口实现都注册一遍,由此,解除了程序的耦合
builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces();
//创建接口容器
IContainer container = builder.Build();
//获取实例
var language = container.Resolve<ILanguage>();
//输出内容
Console.WriteLine(language.GetContent());
}
输出结果:
image.png由于程序集ILanguage结果有多个实现类,依赖注入默认采用覆盖形式,
需要一次性获取多个实现类集合才能依次输出
IEnumerable<ILanguage> languages= container.Resolve<IEnumerable<ILanguage>>();
foreach (var item in languages)
{
//输出内容
Console.WriteLine(item.GetContent());
}
输出结果:
image.png对象的生命周期
- InstancePerDependency (默认) 每次Resolve都会创建新的实例
- SingleInstance (单例):整个根容器的生命周期内是同一个对象,只会实例化一次
- InstancePerLifetimeScope 同一个生命周期获得一样的对象,不同周期有不同对象(想想httpContxt)
static void Main(string[] args){
//创建容器
ContainerBuilder builder = new ContainerBuilder();
//依赖注入 注册成为单例模式
builder.RegisterType<Chinese>().As<ILanguage>().SingleInstance();
//创建接口容器
IContainer container = builder.Build();
//获取对象实例
var language1 = container.Resolve<ILanguage>();
var language2 = container.Resolve<ILanguage>();
//输出内容
Console.WriteLine($"language1 与 language2:{ReferenceEquals(language1, language2)}");
}
输出结果:
image.png
网友评论