C#_深入理解Unity容器
C#_深入理解Unity容器
一、背景
**DIP是依賴倒置原則:**一種軟件架構(gòu)設(shè)計(jì)的原則(抽象概念)。依賴于抽象不依賴于細(xì)節(jié)
**IOC即為控制反轉(zhuǎn)(Inversion of Control):**傳統(tǒng)開發(fā),上端依賴(調(diào)用/指定)下端對(duì)象,會(huì)有依賴,把對(duì)下端對(duì)象的依賴轉(zhuǎn)移到第三方容器(工廠+配置文件+反射),能夠程序擁有更好的擴(kuò)展性,是DIP的具體實(shí)現(xiàn)方式,可以用來(lái)減低計(jì)算機(jī)代碼之間的耦合度。
DI 即為依賴注入(Dependency Injection):
**Unity容器:**是微軟推出的IOC框架,使用這個(gè)框架,可以實(shí)現(xiàn)AOP面向切面編程,便于代碼的后期維護(hù),此外,這套框架還自帶單例模式,可以提高程序的運(yùn)行效率。
二、Nuget下載
Nuget搜索Unity,名稱為Unity的
三、簡(jiǎn)單使用
使用Unity來(lái)管理對(duì)象與對(duì)象之間的關(guān)系可以分為以下幾步:
后面我們用到的方法,其實(shí)大多數(shù)是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接口,我們一個(gè)簡(jiǎn)單的做法就是實(shí)現(xiàn)一個(gè)ICar接口的對(duì)象,通過構(gòu)造函數(shù)傳遞給Driver。
如果使用Unity容器呢?
container.RegisterType<ICar, BMW>();Driver driver = container.Resolve<Driver>();driver.RunCar(); output:Running BMW - 1 mile上例中,我們使用Driver driver = container.Resolve()創(chuàng)造了一個(gè)Driver對(duì)象,但是Driver對(duì)象依賴于ICar。幸運(yùn)的是,我們提前注冊(cè)了ICar的實(shí)現(xiàn)類型為BWM:container.RegisterType<ICar, BMW>()。所以,在創(chuàng)建Driver對(duì)象的時(shí)候自動(dòng)將BWM注入其中了。
1)Multiple Registration
container.RegisterType<ICar, BMW>();container.RegisterType<ICar, Ford>();Driver driver = container.Resolve<Driver>();driver.RunCar(); output:Running Ford - 1 mile同一個(gè)接口ICar注冊(cè)多次的時(shí)候,以最后一次注冊(cè)的類型為準(zhǔn),前面均會(huì)被覆蓋。
2)Register Named Type
//注冊(cè)ICar的時(shí)候給了一個(gè)字符串作為namecontainer.RegisterType<ICar, BMW>("ACar");container.RegisterType<ICar, Ford>();//這里一般會(huì)找那個(gè)沒有name的ICar注入。Driver driver1 = container.Resolve<Driver>();driver1.RunCar();//注冊(cè)Driver的時(shí)候,根據(jù)name取到了注冊(cè)的ICar,通過構(gòu)造函數(shù)注入給了Drivercontainer.RegisterType<Driver>(new InjectionConstructor(container.Resolve<ICar>("ACar")));//如果不通過ICar注冊(cè)Driver,直接在這里使用ICar的name來(lái)獲取Driver是不行的Driver driver = container.Resolve<Driver>();driver.RunCar(); output:Running Ford - 1 mileRunning BMW - 1 mile相同接口的不同實(shí)現(xiàn)可以通過一個(gè)字符串命名做區(qū)分。
3)Register Instance
可以將已經(jīng)存在的對(duì)象注冊(cè)到容器中,每一次使用的時(shí)候都會(huì)是那一個(gè)對(duì)象不會(huì)創(chuàng)建新的對(duì)象。
將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 milecce5e4ed-4acd-4111-a94f-b2f39ac622a6 Running Audi - 2 mile23db5edc-7fb9-4ab7-ba25-172480b4b500 Running Audi - 3 mile上例中,每一次Resolve時(shí),注入的ICar都是同一個(gè)對(duì)象(new Audi()),但是Driver對(duì)象不是同一個(gè)。
2、Constructor Injection
我們知道依賴注入的方式有三種,并且從上面的例子中我們可以看出,Unity的Resolve方法默認(rèn)是構(gòu)造函數(shù)注入的方式。
container.RegisterType<ICar, Audi>();Driver driver = container.Resolve<Driver>();driver.RunCar(); output:58b8aee6-18c5-4ad9-9a50-055ebdcb9980 Running Audi - 1 mile1)Multiple Parameters
添加一個(gè)接口和它的一些實(shí)現(xiàn):
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 mile2)Multiple Constructors
如果Driver有多個(gè)構(gòu)造函數(shù)
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那么在默認(rèn)情況下,使用的就是參數(shù)較多的那一個(gè)構(gòu)造函數(shù)。
想要指定一個(gè)構(gòu)造函數(shù),則有兩種方式:
A、InjectionConstructorAttribute
在想要使用的構(gòu)造函數(shù)上使用InjectionConstructorAttribute特性
[InjectionConstructor]public Driver(ICar car){_car = car;} output:c2cb415e-9d65-47bb-a895-ed0a16a9c8c6 Running Audi with - 1 mile則使用的就是被特性標(biāo)記的構(gòu)造函數(shù)。
B、使用RegisterType()的重載函數(shù)
RegisterType()方法中params InjectionMember[] injectionMembers參數(shù)就是傳遞構(gòu)造函數(shù)的參數(shù)。
container.RegisterType<ICar, Audi>();container.RegisterType<ICarKey, AudiKey>();//注冊(cè)Driver時(shí),使用一個(gè)參數(shù)為ICar類型的構(gòu)造函數(shù)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 mile3、Property Injection
暫不做介紹
4、Method Injection
暫不做介紹
5、Overrides
上面的例子可以看到,我們?cè)赗esolve的時(shí)候,Unity都會(huì)自動(dòng)使用已經(jīng)注冊(cè)了的依賴創(chuàng)建對(duì)象。但是如果不想使用注冊(cè)的依賴呢?
就需要用到ResolverOverride,這是一個(gè)抽象類,有三個(gè)派生類:
舉例一個(gè)override構(gòu)造函數(shù)的參數(shù):
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 mileca39ec80-f227-4e76-94be-67ff20d56a21 Running Ford with FordKey - 1 mile6、Lifetime Managers
Unity容器還可以管理依賴的生命周期,通過RegisterType()方法中的ITypeLifetimeManager參數(shù)。
| 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. |
默認(rèn)情況下是TransientLifetimeManager,每一次都會(huì)Resolve的時(shí)候都會(huì)創(chuàng)建新的依賴。
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 mile9dd3a879-07ed-4508-a061-b5fc85ea40e3 Running Audi with AudiKey - 1 mile使用ContainerControlledLifetimeManager注冊(cè)時(shí),會(huì)注冊(cè)一個(gè)單例的依賴。
```csharpcontainer.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
總結(jié)
以上是生活随笔為你收集整理的C#_深入理解Unity容器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: App中英文切换简单好用
- 下一篇: unity3D埃及探险游戏源码,支持安卓