C#_深入理解Unity容器
C#_深入理解Unity容器
一、背景
**DIP是依賴倒置原則:**一種軟體架構設計的原則(抽象概念)。依賴於抽象不依賴於細節
**IOC即為控制反轉(Inversion of Control):**傳統開發,上端依賴(呼叫/指定)下端物件,會有依賴,把對下端物件的依賴轉移到第三方容器(工廠+配置檔案+反射),能夠程式擁有更好的擴充套件性,是DIP的具體實現方式,可以用來減低計算機程式碼之間的耦合度。
DI 即為依賴注入(Dependency Injection):
- 是實現IOC的手段和方法,就是能做到構造某個物件時,將依賴的物件自動初始化並注入 :
- 有三種注入方式:建構函式注入–屬性注入–方法注入(按時間順序):
- 建構函式注入用的最多,預設找引數最多的建構函式,可以不用特性,可以去掉對容器的依賴
**Unity容器:**是微軟推出的IOC框架,使用這個框架,可以實現AOP面向切面程式設計,便於程式碼的後期維護,此外,這套框架還自帶單例模式,可以提高程式的執行效率。
二、Nuget下載
Nuget搜尋Unity,名稱為Unity的
三、簡單使用
使用Unity來管理物件與物件之間的關係可以分為以下幾步:
- 建立一個UnityContainer物件
- 通過UnityContainer物件的RegisterType方法來註冊物件與物件之間的關係
- 通過UnityContainer物件的Resolve方法來獲取指定物件關聯的物件
IUnityContainer container = new UnityContainer();
後面我們用到的方法,其實大多數是IUnityContainer介面的擴充方法。
1、Register and Resolve
public interface ICar
{
int Run();
}
public class BMW : ICar
{
private int _miles = 0;
public int Run()
{
return ++_miles;
}
}
public class Ford : ICar
{
private int _miles = 0;
public int Run()
{
return ++_miles;
}
}
public class Audi : ICar
{
private int _miles = 0;
public int Run()
{
return ++_miles;
}
}
public class Driver
{
private ICar _car = null;
public Driver(ICar car)
{
_car = car;
}
public void RunCar()
{
Console.WriteLine($"Running {_car.GetType().Name} - {_car.Run()} mile ");
}
}
上面程式碼中,我們可以看到Driver類依賴ICar介面,我們一個簡單的做法就是實現一個ICar介面的物件,通過建構函式傳遞給Driver。
如果使用Unity容器呢?
container.RegisterType<ICar, BMW>();
Driver driver = container.Resolve<Driver>();
driver.RunCar();
output:Running BMW - 1 mile
上例中,我們使用Driver driver = container.Resolve()創造了一個Driver物件,但是Driver物件依賴於ICar。幸運的是,我們提前註冊了ICar的實現型別為BWM:container.RegisterType<ICar, BMW>()。所以,在建立Driver物件的時候自動將BWM注入其中了。
1)Multiple Registration
container.RegisterType<ICar, BMW>();
container.RegisterType<ICar, Ford>();
Driver driver = container.Resolve<Driver>();
driver.RunCar();
output:Running Ford - 1 mile
同一個介面ICar註冊多次的時候,以最後一次註冊的型別為準,前面均會被覆蓋。
2)Register Named Type
//註冊ICar的時候給了一個字串作為name
container.RegisterType<ICar, BMW>("ACar");
container.RegisterType<ICar, Ford>();
//這裡一般會找那個沒有name的ICar注入。
Driver driver1 = container.Resolve<Driver>();
driver1.RunCar();
//註冊Driver的時候,根據name取到了註冊的ICar,通過建構函式注入給了Driver
container.RegisterType<Driver>(new InjectionConstructor(container.Resolve<ICar>("ACar")));
//如果不通過ICar註冊Driver,直接在這裡使用ICar的name來獲取Driver是不行的
Driver driver = container.Resolve<Driver>();
driver.RunCar();
output:Running Ford - 1 mile
Running BMW - 1 mile
相同介面的不同實現可以通過一個字串命名做區分。
3)Register Instance
可以將已經存在的物件註冊到容器中,每一次使用的時候都會是那一個物件不會建立新的物件。
將Driver修改為:
public class Driver
{
private ICar _car = null;
private Guid guid= Guid.NewGuid();
public Driver(ICar car)
{
_car = car;
}
public void RunCar()
{
Console.WriteLine($"{guid.ToString()} Running {_car.GetType().Name} - {_car.Run()} mile ");
}
}
container.RegisterInstance<ICar>(new Audi());
Driver driver = container.Resolve<Driver>();
driver.RunCar();
Driver driver1 = container.Resolve<Driver>();
driver1.RunCar();
Driver driver2 = container.Resolve<Driver>();
driver2.RunCar();
Console.ReadKey();
output:d771409d-1ab5-4a59-b48b-e6b6224e3cd2 Running Audi - 1 mile
cce5e4ed-4acd-4111-a94f-b2f39ac622a6 Running Audi - 2 mile
23db5edc-7fb9-4ab7-ba25-172480b4b500 Running Audi - 3 mile
上例中,每一次Resolve時,注入的ICar都是同一個物件(new Audi()),但是Driver物件不是同一個。
2、Constructor Injection
我們知道依賴注入的方式有三種,並且從上面的例子中我們可以看出,Unity的Resolve方法預設是建構函式注入的方式。
container.RegisterType<ICar, Audi>();
Driver driver = container.Resolve<Driver>();
driver.RunCar();
output:58b8aee6-18c5-4ad9-9a50-055ebdcb9980 Running Audi - 1 mile
1)Multiple Parameters
新增一個介面和它的一些實現:
public interface ICarKey
{
}
public class BMWKey : ICarKey
{
}
public class AudiKey : ICarKey
{
}
public class FordKey : ICarKey
{
}
再將Driver類修改為:
public class Driver
{
private ICar _car = null;
private ICarKey _key = null;
private Guid guid= Guid.NewGuid();
public Driver(ICar car,ICarKey key)
{
_car = car;
_key = key;
}
public void RunCar()
{
Console.WriteLine($"{guid.ToString()} Running {_car.GetType().Name} with {_key.GetType().Name} - {_car.Run()} mile ");
}
}
container.RegisterType<ICar, Audi>();
container.RegisterType<ICarKey, AudiKey>();
Driver driver = container.Resolve<Driver>();
driver.RunCar();
output:e76d9df2-a2aa-45fd-8721-e9fbe607544e Running Audi with AudiKey - 1 mile
2)Multiple Constructors
如果Driver有多個建構函式
public class Driver
{
private ICar _car = null;
private ICarKey _key = null;
private Guid guid= Guid.NewGuid();
public Driver(ICar car,ICarKey key)
{
_car = car;
_key = key;
}
public Driver(ICar car)
{
_car = car;
}
public void RunCar()
{
Console.WriteLine($"{guid.ToString()} Running {_car?.GetType().Name} with {_key?.GetType().Name} - {_car?.Run()} mile ");
}
}
container.RegisterType<ICar, Audi>();
container.RegisterType<ICarKey, AudiKey>();
Driver driver = container.Resolve<Driver>();
driver.RunCar();
output:6681c7e2-0235-495e-b52f-2457f58dc6d7 Running Audi with AudiKey - 1 mile
那麼在預設情況下,使用的就是引數較多的那一個建構函式。
想要指定一個建構函式,則有兩種方式:
A、InjectionConstructorAttribute
在想要使用的建構函式上使用InjectionConstructorAttribute特性
[InjectionConstructor]
public Driver(ICar car)
{
_car = car;
}
output:c2cb415e-9d65-47bb-a895-ed0a16a9c8c6 Running Audi with - 1 mile
則使用的就是被特性標記的建構函式。
B、使用RegisterType()的過載函式
RegisterType()方法中params InjectionMember[] injectionMembers引數就是傳遞建構函式的引數。
container.RegisterType<ICar, Audi>();
container.RegisterType<ICarKey, AudiKey>();
//註冊Driver時,使用一個引數為ICar型別的建構函式
container.RegisterType<Driver>(new InjectionConstructor(container.Resolve<ICar>()));
Driver driver = container.Resolve<Driver>();
driver.RunCar();
output:3488a2a1-2cff-45a9-99a6-0fb5dfdc6fa2 Running Audi with - 1 mile
3、Property Injection
暫不做介紹
4、Method Injection
暫不做介紹
5、Overrides
上面的例子可以看到,我們在Resolve的時候,Unity都會自動使用已經註冊了的依賴建立物件。但是如果不想使用註冊的依賴呢?
就需要用到ResolverOverride,這是一個抽象類,有三個派生類:
- ParameterOverride: Used to override constructor parameters.
- PropertyOverride: Used to override the value of a specified property.
- DependencyOverride: Used to override the type of dependency and its value
舉例一個override建構函式的引數:
container.RegisterType<ICar, Audi>();
container.RegisterType<ICarKey, AudiKey>();
Driver driver = container.Resolve<Driver>();
driver.RunCar();
Driver driver1 = container.Resolve<Driver>(new ResolverOverride[]
{
new ParameterOverride("car",new Ford()),
new ParameterOverride("key",new FordKey()),
});
driver1.RunCar();
output:bb8f06bc-6fed-4b32-9c7d-03d33ebd1b19 Running Audi with AudiKey - 1 mile
ca39ec80-f227-4e76-94be-67ff20d56a21 Running Ford with FordKey - 1 mile
6、Lifetime Managers
Unity容器還可以管理依賴的生命週期,通過RegisterType()方法中的ITypeLifetimeManager引數。
Lifetime Manager | Description |
---|---|
TransientLifetimeManager | Creates a new object of the requested type every time you call the Resolve or ResolveAll method. |
ContainerControlledLifetimeManager | Creates a singleton object first time you call the Resolve or ResolveAll method and then returns the same object on subsequent Resolve or ResolveAll calls. |
HierarchicalLifetimeManager | Same as the ContainerControlledLifetimeManager, the only difference is that the child container can create its own singleton object. The parent and child containers do not share the same singleton object. |
PerResolveLifetimeManager | Similar to the TransientLifetimeManager, but it reuses the same object of registered type in the recursive object graph. |
PerThreadLifetimeManager | Creates a singleton object per thread. It returns different objects from the container on different threads. |
ExternallyControlledLifetimeManager | It maintains only a weak reference of the objects it creates when you call the Resolve or ResolveAll method. It does not maintain the lifetime of the strong objects it creates, and allows you or the garbage collector to control the lifetime of the objects. It enables you to create your own custom lifetime manager. |
預設情況下是TransientLifetimeManager,每一次都會Resolve的時候都會建立新的依賴。
container.RegisterType<ICar, Audi>(new TransientLifetimeManager());
container.RegisterType<ICarKey, AudiKey>();
Driver driver = container.Resolve<Driver>();
driver.RunCar();
Driver driver1 = container.Resolve<Driver>();
driver1.RunCar();
output:275eb0e4-b76b-4ac2-a15e-cb899adefb03 Running Audi with AudiKey - 1 mile
9dd3a879-07ed-4508-a061-b5fc85ea40e3 Running Audi with AudiKey - 1 mile
使用ContainerControlledLifetimeManager註冊時,會註冊一個單例的依賴。
container.RegisterType<ICar, Audi>(new ContainerControlledLifetimeManager());
container.RegisterType<ICarKey, AudiKey>();
Driver driver = container.Resolve<Driver>();
driver.RunCar();
Driver driver1 = container.Resolve<Driver>();
driver1.RunCar();
output:4b9a8be6-1e20-42f9-942b-32919c4f55b4 Running Audi with AudiKey - 1 mile
6bb64e7d-c85a-4bc7-a03a-67f937877753 Running Audi with AudiKey - 2 mile
其他的暫不做解釋
相關文章
- 深入理解Spring IOC容器Spring
- Laravel Container (容器) 深入理解 (下)LaravelAI
- 深入理解DIP、IoC、DI以及IoC容器
- SpringIOC二—— 容器 和 Bean的深入理解SpringBean
- 【Java】Java容器篇(二),深入理解List集合類Java
- 深入理解Spring IOC容器及擴充套件Spring套件
- 深入理解虛擬機器、容器和Hyper技術虛擬機
- Docker | Docker技術基礎梳理(四) - 深入理解映象與容器Docker
- 【Unity3D】UI Toolkit容器Unity3DUI
- 深入Laravel服務容器Laravel
- 深入理解spring容器中的控制反轉(IOC)和依賴注入(DI)Spring依賴注入
- 深入剖析 Laravel 服務容器Laravel
- 深入理解Isolate
- 深入理解HashMapHashMap
- 深入理解TransformORM
- 深入理解KVO
- 深入理解 JVMJVM
- 深入理解 GitGit
- 深入理解AQSAQS
- 深入理解JVMJVM
- 深入理解 TypeScriptTypeScript
- 深入理解JavaScriptCoreJavaScript
- 深入理解MVCMVC
- 深入理解 PWA
- 深入理解margin
- 深入理解ReactReact
- 深入理解BFC
- 深入理解reduxRedux
- BFC深入理解
- 深入理解 GCDGC
- 深入理解 requestAnimationFramerequestAnimationFrame
- 深入理解Eureka
- 深入理解copy
- AsyncTask深入理解
- 深入理解RunLoopOOP
- 深入理解Fsync
- 深入理解yield
- 如何理解 Laravel 的 IoC 容器Laravel