StyletIoC 配置
在这里,您将学习如何创建一个新的StyletIoC容器,并在其中注册您的服务。
入门-生成器
若要创建容器,您必须创建一个新的StyletIoCBuilder
,并在其上注册所有服务。StyletIoCBuilder.BuildContainer()
将被调用,因此您不需要调用它来构建容器。
例如:
// 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>();
创建类型的不同方法
如前所述,StyletIoC的目的是接受对服务类型的请求,并使用您使用StyletIoCBuilder
配置的规则返回实现该服务的某个实例。不过,它可以使用几种不同的技术来创建该实例。
类型绑定
最简单的称为“类型绑定”。你告诉它服务类型,以及实现该服务的类型,它就会知道如何构造该类型本身。例如:
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());
实例绑定
您可以自己构造一个类型的实例,并将其提供给IoC容器。这对于配置对象之类的事情可能很有用。例如:
builder.Bind<IVehicle>().ToInstance(new HotHatchback());
实例绑定是自动单例的——容器不知道如何构造该类型的新实例,因此必须始终返回相同的实例。
默认情况下,IoC容器在被释放时会释放IDisposable
实例类型,但您可以使用.ToInstance(foo).DisposeWithContainer(false);
进行配置。
不同的范围
瞬态范围
默认情况下,StyletIoC每次请求时都会创建一个给定类型的新实例,这被称为“瞬态范围”。请参阅:
var car1 = ioc.Get<IVehicle>();
var car2 = ioc.Get<IVehicle>();
// The following statement will succeed.
Assert.AreNotEqual(car1, car2);
IoC容器不会处理IDisposable
瞬态类型——它不会声称对它们的所有权,也不知道什么时候应该处理它们。
单例范围
然而,您也可以告诉它永远只创建一个服务实例,并在每次请求时返回该实例。这被称为“singleton scope”,如果您的应用程序确实有singleton(大多数都有),这将非常有用,因为它可以使您免于拥有传统的singleton对象,而传统的singleton对象在单元测试中很难模拟。
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();
请注意,singleton的作用域是定义它们的绑定处,请参阅:
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();
IoC容器在被处理时将处理IDisposable
单例。
将多个类型绑定到单个服务
到目前为止,我们已经尝试将单个类型绑定到单个服务。但事实并非如此。您可以将多个类型绑定到单个服务,例如:
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
提取一个IVehicle
,StyletIoC不知道该给你哪个,所以它抛出了一个异常。
这也适用于构造函数和参数注入,请参阅:
class Garage
{
public Garage(IEnumerable<IVehicle> vehicles) { ... }
}
// And
class Garage
{
[Inject]
public IEnumerable<IVehicle> Vehicles { get; set; }
}
绑定泛型类型
StyletIoC处理与普通类型相同的绑定泛型类型(其中类型参数的确切类型是已知的),例如:
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>>(); // 返回一个 Validator<int>
服务及其实现可以具有任意多的类型参数,但它们必须具有相同数量的类型参数(如果仔细考虑,这是有意义的)。然而,类型参数可以以任何顺序出现:
interface ISomeInterface<T, U> { ... }
class SomeClass<U, T> : ISomeInterface<T, U> { ... }
builder.Bind(typeof(ISomeInterface<,>)).To(typeof(SomeClass<,>));
StyletIoC不考虑类型约束-如果您有一个interface IMyInterface<T> where T:class
并且请求IMyInterface<int>
,你将会得到一个异常。
自动绑定
StyletIoC能够自动为您创建绑定。
自动绑定所有具体类型
自动绑定意味着,如果您请求尚未向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上定义了两个扩展方法,这可能会使您的生活更轻松:
// 返回所有基础类型
someType.GetBaseTypes();
// 返回所有基础类型和接口
someType.GetBaseTypesAndInterfaces();
// 如果 someType 实现了 someServiceType,则返回 true
someType.Implements(someServiceType)
// 还考虑了泛型——所以这是真的:
typeof(Validator<int>>.Implements(typeof(IValidator<>));