简介
StyletIoC轻量级的极快速的IoC容器。
服务和构建
StyletIoC是围绕服务的概念构建的。服务是具体类型、抽象类型或接口,由具体类型实现,例如:
interface IVehicle { ... }
class HotHatchback : IVehicle { ... }
这里IVehicle是服务,HotHatchback是实现它的具体类型。请注意,HotHatchback也是一个服务 - 由类本身实现的服务。
配置 StyletIoC 时,需要定义一组关系。每个关系都介于服务与实现它的类型(或多个类型)之间。所以在这里,我们可以告诉StyletIoC"在服务IVehicle和类型HotHatchback之间建立一种关系"。
稍后,当你想要实现IVehicle时,你可以要求StyletIoC"给我一个实现IVehicle服务的实例,StyletIoC将构造一个HotHatchback并将其传回给你。
解析类型 - 服务定位器和注入
有3种方法可以让StyletIoC为我们构建一个类型:
- 通过直接调用IContainer.Get
- 构造函数注入
- 属性注入
直接调用IContainer.Get,如下例:
var ioc = ... // Covered in lots of detail elsewhere
var vehicle = ioc.Get<IVehicle>();
看起来很简单,但是缺点是只能在应用程序的根目录才能这样做,在其他地方还是需要使用构造函数和参数注入。
构造函数注入的例子
class Engine { ... }
class Car
{
public Car(Engine engine)
{
// 'engine' contains a new instance of Engine
}
}
属性注入的例子:
class Engine { ... }
class Car
{
[Inject]
public Engine Engine { get; set; }
}
StyletIoC Configuration
入门 - 构建器
使用StyletIoC容器的方法是首先调用StyletIoCBuilder构建容器,然后在其上注册服务。例如:
// First, create the builder
var builder = new StyletIoCBuilder();
// Configure the builder
builder.Bind<IVehicle>().To<HotHatchback>();
// The container is then built for you
// Now you can use this to resolve instances of services
var vehicle = ioc.Get<IVehicle>();
创建类型的不同方式
类型绑定
代码如下:
builder.Bind<IVehicle>().To<HotHatchback>();
这表示告知StyletIoC:每当要求提供一个IVehicle的实现,就使用适当的构造函数创建一个HotHatchback的新实例,并将所必要的依赖传入。
也可以将服务绑定到自身,这是一种类型约束,称之为“自绑定”,例如:
builder.Bind<HotHatchback>().To<HotHatchback>();
// Or, more clearly:
builder.Bind<HotHatchback>().ToSelf();
这表示告知StyletIoC:每当要求提供一个HotHatchback的实现,就给出一个早已包含所有依赖的HotHatchback。
也可以使用非范型版本:
builder.Bind(typeof(IVehicle)).To(typeof(HotHatchback));
工厂绑定
可以告知StyletIoC如何构建类型,传入一个委托,称之为“工厂缄定”,例如:
builder.Bind<IVehicle>().ToFactory(container => new HotHatchback());
这里的container参数是IContainer类型,可以用来注入构造函数中必要的依赖,例如:
builder.Bind<IVehicle>().ToFactory(container => new HotHatchback(container.Get<Engine>()));
当然,这里也支持自绑定:
builder.Bind<HotHatchback>().ToFactory(container => new HotHatchback());
实例绑定
也可以将一个自己创建的实例注入容器,例如:
builder.Bind<IVehicle>().ToInstance(new HotHatchback());
实例绑定原生是单例的,因为容器不知道如何构建该类型的新实例,因此始终返回相同的实例。
作用范围
Transient
默认情况下,StyletIoC每次都会创建一个新的实例,称之Transient:
var car1 = ioc.Get<IVehicle>();
var car2 = ioc.Get<IVehicle>();
// The following statement will succeed.
Assert.AreNotEqual(car1, car2);
IoC容器不会主动释放Transient实例,如果需要,请手动处理。
单例
你可以告知IoC仅创建唯一一个服务的实例,这有利于单元测试:
builder.Bind<IConfigurationManager>().To<ConfigurationManager>().InSingletonScope();
var ioc = builder.BuildContainer();
var configManager1 = ioc.Get<IConfigurationManager>();
var configManager2 = ioc.Get<IConfigurationManager();
// The following statement will succeed.
Assert.AreEqual(configManager1, configManager2);
当然也可以使用工厂绑定方式构建单例:
builder.Bind<IConfigurationManager>().ToFactory(container => new ConfigurationManager()).InSingletonScope();
采用不同方式构建的单例并不是同一对象:
builder.Bind<IConfigurationManager>().To<ConfigurationManager>().InSingletonScope();
builder.Bind<ConfigurationManager>().ToSelf();
var ioc = builder.BuildContainer();
var configManager1 = ioc.Get<IConfigurationManager>();
var configManager2 = ioc.Get<ConfigurationManager>();
// The following statement will succeed.
Assert.AreNotEqual(configManager1, configManager2);
如果你想要让其返回同一实例,可以这样做:
builder.Bind<ConfigurationManager>().And<IConfigurationManager>().To<ConfigurationManager>().InSingletonScope();
将多个类型绑定到单一服务
例如:
interface IVehicle { ... }
class HotHatchback : IVehicle { ... }
class OldBanger : IVehicle { ... }
builder.Bind<IVehicle>().To<HotHatchback>();
builder.Bind<IVehicle>().To<OldBanger>();
var ioc = builder.BuildContainer();
// This will throw an exception
ioc.Get<IVehicle>();
// This will return { new HotHatchback(), new OldBanger() }
IEnumerable<IVehicle> vehicles = ioc.GetAll<IVehicle>();
如果需要获取集合,应该使用IContainer.GetAll方法,使用IContainer.Get,因为无法判断是哪一个,会抛出异常。
这个规则同样适用于构造函数及参数注入:
class Garage
{
public Garage(IEnumerable
}
// And
class Garage
{
[Inject]
public IEnumerable
}
绑定范型
范型绑定的方法与上面类似:
interface IValidator<T> { ... }
class IntValidator : IValidator<int> { ... }
builder.Bind<IValidator<int>>().To<IntValidator>();
interface IValidator<T> { ... }
class Validator<T> : IValidator<T> { ... }
builder.Bind<IValidator<int>>().To<Validator<int>>();
有趣的是,可以为未知的泛型类型创建绑定:
interface IValidator<T> { ... }
class Validator<T> : IValidator<T> { ... }
builder.Bind(typeof(IValidator<>)).To(typeof(Validator<>));
var ioc = builder.BuildContainer();
var intValidator = ioc.Get<IValidator<int>>(); // Returns a Validator<int>
服务及其实现都可以有任意数量的类型参数,但它们必须有相同数量的类型参数(如果你仔细考虑的话,这是有道理的)。 但是,类型参数可以按任何顺序出现:
interface ISomeInterface<T, U> { ... }
class SomeClass<U, T> : ISomeInterface<T, U> { ... }
builder.Bind(typeof(ISomeInterface<,>)).To(typeof(SomeClass<,>));
StyletIoC 不考虑类型约束——如果你有一个接口 IMyInterface
自动绑定
自动绑定所有实体类型
自动绑定意味着如果您请求一个尚未向 Stylet 注册的具体类型,Stylet 将尝试为您构建它作为临时实例。 它仅适用于您指定的程序集中的类型:StyletIoCBuilder.Assemblies 中的类型,以及您传递给 Autobind 方法的任何程序集中的类型。
请注意,显式绑定始终优先于自动绑定。
将诸如 mscorlib 之类的程序集传递给 Autobind 是一个坏主意,否则 Stylet 将尝试实例化诸如 System.String 之类的类型。
builder.Autobind();
这在 MVVM 应用程序中很有用,因为它允许 StyletIoC 解析您的任何 ViewModel。 Stylet 引导程序调用 Autobind(),这意味着默认情况下启用自动绑定。
绑定一个服务到所有实现
interface IVehicle { ... }
class HotHatchback : IVehicle { ... }
class OldBanger : IVehicle { ... }
builder.Bind<IVehicle>().ToAllImplementations();
var ioc = builder.BuildContainer();
IEnumerable<IVehicle> vehicles = ioc.GetAll<IVehicle>(); // Returns { new HotHatchback(), new OldBanger() }
还有一些重载允许您指定要搜索的程序集。
这本身就很有用(想想查找所有插件),但在与未绑定的泛型结合使用时特别有用。 例如:
interface IValidator<T> { ... }
class IntValidator : IValidator<int> { ... }
class StringValidator : IValidator<string> { ... }
builder.Bind(typeof(IValidator<>)).ToAllImplementations();
var ioc = builder.BuildContainer();
var intValidator = ioc.Get<IValidator<int>>(); // Returns an IntValidator
var stringValidator = ioc.Get<IValidator<string>>(); // Returns a StringValidator
如果你想要更复杂的绑定规则,StyletIoC 并没有为你提供 API——你自己做几乎不需要任何努力,而提供 API 只是增加了很多复杂性而收效甚微。
但是,StyletIoC 确实在 Type 上定义了一些扩展方法,这可能会让您的生活更轻松:
// Returns all base types
someType.GetBaseTypes();
// Returns all base types and interfaces
someType.GetBaseTypesAndInterfaces();
// Returns true if someType implements someServiceType
someType.Implements(someServiceType)
// Also takes into account generics - so this is true:
typeof(Validator<int>>.Implements(typeof(IValidator<>));