Reference:
使用Unity解耦你的系统—PART2——了解Unity的使用方法(1)
一、初始化UnityContainer
-
- 使用API
IUnityContainer container = new UnityContainer()
// Register 映射。
- 使用配置文件
<configSections> <section name=”unity” type=”Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration”/> </configSections>
<unity xmlns=”http://schemas.microsoft.com/practices/2010/unity”>
<alias alias=”IClass” type=”UnityStudyConsole.IDemo.IClass, UnityStudyConsole” />
<container name=”First”>
<register type=”IClass” mapTo=”MyClass” />
</container>
</unity>
IUnityContainer container = new UnityContainer();
// 使用LoadConfiguration默认使用unity section加载unity(无名字)节点。
container.LoadConfiguration();
// 获取section,加载有名字的unity配置。
UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection(“unity”);
container.LoadConfiguration(section, “First”);
参考:
二、注册
-
- 使用RegisterType
container .RegisterType<IStocksTickerView, StocksTickerForm>() .RegisterType<IStockQuoteService, MoneyCentralStockQuoteService>();
注:使用RegisterType注册的默认生命周期为transient lifetime(每次使用Resolve或者ResolveAll或者属性注入的时候都实例化一个新实例)。
- 使用RegisterInstance
// 注册实例,“UI”为实例名。
RegisterInstance(new TraceSource(“UI”, SourceLevels.All));
// 注册接口与实例的映射(MySingleton为映射名)
myContainer.RegisterInstance<IMyObject>(“MySingleton”, MyRealObject);
// 注册带有生命周期的接口与实例的映射。
myContainer.RegisterInstance<IMyObject>(“MySingleton”, MyRealObject, new ContainerControlledLifetimeManager());
注:使用RegisterInstance注册的实例的生命周期为ContainerControlledLifetimeManager;使用该方法无法为实例制定TransientLifetimeManager生命周期,否则会产生异常。
- 使用RegisterType
- 属性注入(方法与构造函数注入类似)
- 【使用标注】在属性前加[Dependency]标注,如:
[Dependency]
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}对于实体类,解析的时候可以使用默认的规则解析。对于接口活虚类,需要注册。如:
RegisterType<ILogger, ConsoleLogger>();
使用带有名字的的属性注入,如:
[Dependency(“UI”)]
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}注册带有名字的Logger,如:
RegisterType<ILogger, TraceSourceLogger>(“UI”);
- 【使用标注】在属性前加[Dependency]标注,如:
- 【使用API】使用API进行属性注入
注册不带名字的属性
RegisterType<IStockQuoteService, MoneyCentralStockQuoteService>( new InjectionProperty(“Logger”))
注:其中Logger为MoneyCentralStockQuoteService类中的一个类型为ILogger的属性。
注入带有名字的属性:
RegisterType<StocksTickerPresenter>( new InjectionProperty(“Logger”, new ResolvedParameter<ILogger>(“UI”)));
注:其中new InjectionProperty第二参数是指示要解析一个名字为“UI”的ILogger。
注册多个属性:
//注册对象关系时初始化对象的属性 container.RegisterType<IClass, MyClass>( new InjectionProperty("Name", "A班"), new InjectionProperty("Description", "A班的描述"));
重载调用:
//var myClass = container.Resolve<IClass>(new PropertyOverride
// ("Name", "重载后的A班"), // new PropertyOverride("Description", "重载后的A班的描述")); var myClass = container.Resolve<IClass>(new PropertyOverrides() { {"Name","重载后的A班"}, {"Description","重载后的A班的描述"} }.OnType<MyClass>());
注意:The PropertyOverride is not limited to a single type,
so it will apply to all resolved objects with a “Logger” property.
参考:本篇中的构造函数注入
构造函数注入
- 【使用标注】如果一个类有多个构造函数,则需要在使用的构造参数前加标注:
[InjectionConstructor]
- 【使用API】 注入构造函数
RegisterType<ILogger, TraceSourceLogger>( “UI”, new InjectionConstructor(“UI”))
注:其中第一个参数“UI”为ILogger, TraceSourceLogger的映射名,第二个参数new InjectionConstructor(“UI”)为构造函数传入一个字符串类型的参数“UI”。如此,TraceSourceLogger将调用一个带有string类型的构造函数。
使用这个API注入比用“InjectionConstructor”标注的构造函数的优先级别更高。
如:
container.RegisterType<IClass, YourClass>( new InjectionConstructor("my", new MyClass())); container.Resolve<IClass>();
在调用的时候我们想更换原先注册的值,可以通过ParameterOverride和ParameterOverrides
来实现,其中ParameterOverride是针对一个参数,而ParameterOverrides是针对参数列表:
DependencyOverride// 在使用ParameterOverrides进行重载参数时,
// 可以使用如上面代码的方式进行指定,但是同样需要使用OnType来指定,
// 不过这个的OnType指定的类型是注册的对象类型。
container.Resolve<IClass>(new ParameterOverrides() { {"test","test"}, {"my",new MyClass()} }.OnType<YourClass>());// 在使用ParameterOverride方法来重载参数时,如果注册的参数是一个
// 具体的对象就需要使用OnType这个扩展方法来指定对应的类型,否则会报错。
container.Resolve<IClass>(new ParameterOverride("test", "test"),new ParameterOverride("my", "new MyClass").OnType<MyClass>());
Unity还为我们提供了一个DependencyOverride重载,其使用方法和参数重载、属性重载类似,这边就不演示了,不过需要注意的是DependencyOverride是针对所注册对象类型中所包含的对象类型重载,例如在A类中有构造函数参数是B类,同时也有个属性依赖于B类,当使用了DependencyOverride后,这个A对象原先注册的有关B类的依赖将全部改变。
注册泛型- 使用API
container .RegisterType( typeof(IRepository<>), typeof(ValidatingRepository<>), “validating”);
注意:open generic types cannot be used as generic type arguments。
可以在开闭泛型之间建立映射,也可以在闭映射和非泛型之间建立映射,如:
RegisterType<IValidator<StockQuote>, RandomStockQuoteValidator>();
使用overrides解析泛型:
使用配置文件StocksTickerPresenter presenter = container.Resolve<StocksTickerPresenter>( new ParameterOverride( “repository”, new ResolvedParameter<IRepository<StockQuote>>(“validating”)) .OnType<StocksTickerPresenter>());
闭泛型
<register type=”IRepository[StockQuote]” mapTo=”DebugRepository[StockQuote]“/>
开泛型
<register type=”IRepository[]” mapTo=”DebugRepository[]“/>
例:
<register type=”IRepository[]” mapTo=”DebugRepository[]“/>
public StocksTickerPresenter( IStocksTickerView view, IStockQuoteService stockQuoteService, IRepository<StockQuote> repository) { … }
解析IRepository<StockQuote>过程:首先查找是否有相应的闭泛型,如果有,则进一步解析构造函数,…;如果没有,则查找开泛型,IRepository<>被找到,此时,开泛型DebugRepository<>被使用,StockQuote被传作泛型参数,解析完毕。
数组注入CompositeLogger的构造函数为:
public CompositeLogger(ILogger[] loggers)
{
this.loggers = (IEnumerable<ILogger>)loggers.Clone();
}
注册方法为:
RegisterType<ILogger, CompositeLogger>( “composite”, new InjectionConstructor( new ResolvedArrayParameter<ILogger>( typeof(ILogger), new ResolvedParameter<ILogger>(“UI”))));
其中:第一个参数typeof(ILogger是new ResolvedParameter<ILogger>())的简写。
注意:当解析Array时,只有有名字的实例会被包含进来。
三、解析
-
- 语法
StocksTickerPresenter presenter = container.Resolve<StocksTickerPresenter>();
注:如果StocksTickerPresenter的构造函数有参数,则需要先注册参数。
- 延迟获取对象
public static void DeferringResolve() { var resolver = container.Resolve<Func<IClass>>(); //注册IClass与MyClass之间的关系 container.RegisterType<IClass, MyClass>(); //获取MyClass实例 var myClass = resolver(); var resolver2 = container.Resolve<Func<IEnumerable<IClass>>>(); //注册与IClass相关的对象。 container.RegisterType<IClass, MyClass>("my"); container.RegisterType<IClass, YourClass>("your"); //获取与IClass关联的所有命名实例 var classList = resolver2(); }
-
- 检索容器中注册信息
public static void DisplayContainerRegistrations(
IUnityContainer theContainer) { string regName, regType, mapTo, lifetime; Console.WriteLine("容器中 {0} 个注册信息:", theContainer.Registrations.Count()); foreach (ContainerRegistration item in theContainer.Registrations) { regType = item.RegisteredType.Name; mapTo = item.MappedToType.Name; regName = item.Name ?? "[默认]"; lifetime = item.LifetimeManagerType.Name; if (mapTo != regType) { mapTo = " -> " + mapTo; } else { mapTo = string.Empty; } lifetime = lifetime.Substring(0, lifetime.Length -
"生命周期管理器".Length); Console.WriteLine("+ {0}{1} '{2}' {3}", regType, mapTo,
regName, lifetime); } }
四、生命周期
-
- 使用生命周期
- 使用API
RegisterType<ILogger, TraceSourceLogger>( “UI”, new ContainerControlledLifetimeManager(), new InjectionConstructor(“UI”))
- 使用配置文件
<register type="IClass" mapTo="MyClass"> <lifetime type="transient" /> <!--<lifetime type="SessionLifetimeManager" value="Session#1" typeConverter="SessionLifetimeConverter" />
--> </register><br>
- 使用生命周期
- ContainerControlledLifetimeManager:维护一个对象实例的强引用,每次调用的时候都会返回同一对象。
- ExternallyControlledLifetimeManager:作用类似于singleton,但是当实例没有被引用的时候,垃圾回收期将回收这个实例。 如果被GC回收后再次调用Resolve方法将会重新创建新的对象。调用GC的代码如下:
GC.Collect();
配置文件如下:
PerThreadLifetimeManager:每个线程拥有一个实例。对于一个线程来说,作用类似于singleton。 一般来说不建议在使用RegisterInstance对已存在的对象注册关系时使用PerThreadLifetimeManager,因为此时的对象已经在一个线程内创建了,如果再使用这个生命周期管理器,将无法保证其正确调用。<register type=”IClass” mapTo=”MyClass” name=”ccl”>
<lifetime type=”external” />
</register>
TransientLifetimeManager:每次调用Resolve or ResolveAll或者属性注入的时候都实例化一个新实例时创建新实例。<register type=”IClass” mapTo=”MyClass” name=”ccl”>
<lifetime type=”perthread” />
</register>
HierarchicalLifetimeManager:类似于ContainerControlledLifetimeManager,不同之处在于父容器和子容器维护各自的生命周期。也就是说,用子容器注册一个映射之后,用父容器和子容器解析解析出来的实例是不同的。Unity这种分级容器的好处就在于我们可以对于有不同生命周期的对象放在不同的容器中,如果一个子容器被释放,不会影响到其它子容器中的对象,但是如果根节点处父容器释放后,所有的子容器都将被释放。<register type=”IClass” mapTo=”MyClass” name=”ccl”>
<lifetime type=”transient” />
</register>
<register type="IClass" mapTo="MyClass" name="hl"> <lifetime type="hierarchical"> </register>PerResolveLifetimeManager:类似于TransientLifetimeManager,区别在于在第一调用的时候会创建一个新的对象,而再次通过循环引用访问到的时候就会返回先前创建的对象实例。如:
{ [Dependency] public IPresenter Presenter { get; set; } } public class MockPresenter : IPresenter { [Dependency] public IView View { get; set; } } 解析IView得到的实例Presenter 中的View与第一次解析IView的实例为同一个。