DryIoc框架中关于类型定义两个术语: Implementaion Type (“实现类”) 与 Service Type (”服务类“) 。简单来说,Service Type 通常是指抽象类或接口,Implementation Type 是 Service Type 的实现类。
注册 API
示例 :注册“服务类”与“实现类”之间的一一映射
// 支持编译时类型
class Register_service_with_implementation_types
{
[Test]
public void Example()
{
var container = new Container();
container.Register<IClient, SomeClient>();
container.Register<IService, SomeService>();
}
}
// 支持运行时类型,通过传入 Type 类型参数
class Register_service_with_implementation_runtime_types
{
[Test]
public void Example()
{
var container = new Container();
container.Register(typeof(IClient), typeof(SomeClient));
container.Register(typeof(IService), typeof(SomeService));
}
}
// 支持泛型
class Register_open_generic_service_with_implementation_runtime_types
{
interface IService<T> { }
class SomeService<T> : IService<T> { }
[Test]
public void Example()
{
var container = new Container();
container.Register(typeof(IService<>), typeof(SomeService<>));
// 支持自动识别泛型的类型参数 T
var s = container.Resolve<IService<string>>();
Assert.IsInstanceOf<SomeService<string>>(s);
}
}
// 支持将“实现类” 注册为 ”服务类”
class Register_implementation_as_service_type
{
[Test]
public void Example()
{
var container = new Container();
container.Register<SomeService>();
// or via run-time type
container.Register(typeof(SomeService));
}
}
最顶层的注册 API
Container 类所有的 Register 重载方法最终都是在调用该接口方法。
从 IRegistrator.Register 方法的声明可知,“服务类”作为直接参数,而“实现类”并不作为直接参数
public interface IRegistrator
{
//..
void Register(Factory factory, Type serviceType, object serviceKey, IfAlreadyRegistered? ifAlreadyRegistered, bool isStaticallyChecked);
}
Register
源码: 从DryIoc 源码可知,“实现类”是作为 Factory 类的构造函数的参数
// DryIco源码:src/DryIoc/Container.cs
public static void Register<TService, TImplementation>(this IRegistrator registrator,
IReuse reuse = null, Made made = null, Setup setup = null, IfAlreadyRegistered? ifAlreadyRegistered = null,
object serviceKey = null)
where TImplementation : TService =>
registrator.Register(new ReflectionFactory(typeof(TImplementation), reuse, made, setup),
typeof(TService), serviceKey, ifAlreadyRegistered, isStaticallyChecked: true);
Factory 类
Factory 类,使用不同的方式实例化服务对象,Register 方法 默认使用的是 ReflectionFactory 。
DryIoc 一共提供了三种 Factory类型,分别是:
- ReflectionFactory:根据类型进行反射构建对象。
- DelegateFactory:根据委托方法构建对象。
- ExpressionFactory:根据表达式构建对象。
IReuse 接口
IReuse 接口,用于简化对象复用策略。
使用例子:
- 在构建 Factory 类对象时,可通过传入Reuse.Singleton 对象(Singleton 单例模式)。
- Reuse.Singleton 本质是 IReuse 接口类型的静态实例。
SingletonReuse 源码: ```csharp // DryIco源码:src/DryIoc/Container.cs public sealed class SingletonReuse : IReuse {…}container.Register(typeof(IA), new ReflectionFactory(typeof(A), Reuse.Singleton));
// DryIco源码:src/DryIoc/Container.cs public static class Reuse { ///
<a name="xl69m"></a>
# 注册单例模式
在上述例子中,注册时并不关注服务对象的生命周期,意味着这些服务实例l都是瞬时(Transient)—— 每次解析注入时都要要创建一个服务实例。
注意:通常,容器的生命周期与程序的生命周期保持一直。
DryIoc 支持通过 Reuse 与 Scopes 实现单例模式。
<a name="xzuuI"></a>
#### 示例 :单例模式
```csharp
class Singleton_service_registration
{
[Test]
public void Example()
{
var container = new Container();
container.Register<IClient, SomeClient>();
// 这里使用 Reuse.Singleton 将该注册类型声明为 单例模式
container.Register<IService, SomeService>(Reuse.Singleton);
var one = container.Resolve<IClient>();
var two = container.Resolve<IClient>();
Assert.AreSame(two.Service, one.Service);
}
}
注册多个服务类型
DryIoc 支持两种方式用于注册多个服务类型:默认注册方式、携带Key注册方式。
默认注册方式
示例 :注册“服务类”与“实现类”之间的一对多映射。
class Multiple_default_registrations
{
internal interface ICommand { }
internal class GetCommand : ICommand { }
internal class SetCommand : ICommand { }
internal class DeleteCommand : ICommand { }
[Test]
public void Example()
{
var container = new Container();
// 依次为 ICommand 注册3个类型
container.Register<ICommand, GetCommand>();
container.Register<ICommand, SetCommand>();
container.Register<ICommand, DeleteCommand>();
// Assert通过,执行代码抛出错误,因为 ICommand 接口已注册了三个“实现类”,导致 container 无法判断解析对象的类型
Assert.Throws<ContainerException>(() =>
container.Resolve<ICommand>());
// 注册了多个“实现类”时,解析对象可以使用集合进行封装,例如IEnumberable<T>。
var commands = container.Resolve<IEnumerable<ICommand>>();
// 集合中的每一项都与对应一种“实现类”,集合数量等于注册的“实现类”的数量。
Assert.AreEqual(3, commands.Count());
// 直接使用 ResolveMany 方法,即可直接解析出集合类型,默认返回类型为 IEnumerable<T>
var commands2 = container.ResolveMany<ICommand>();
Assert.AreEqual(3, commands2.Count());
}
}
DryIoc默认支持的集合类型:
- IEnumerable
(默认) - T[]
- IList
- ICollection
- IReadOnlyList
- IReadOnlyCOllection
- 通过手动设置集合类型,可更改解析集合类型,例如:(除了IEnumerable外,其余接口类型均解析出数组类型Array )
为了方便,DryIoc 定义了专用的、包含默认参数的ResolveMany方法。// 解析出数组集合
var commands1 = container.Resolve<ICommand[]>();
var commands2 = container.Resolve<IList<ICommand>>();
var commands3 = container.Resolve<ICollection<ICommand>>();
var commands4 = container.Resolve<IReadOnlyList<ICommand>>();
var commands5 = container.Resolve<IReadOnlyCOllection<ICommand>>();
为了在内部存储这些注册信息,DryIoc 使用了 DefaultKey 类型作为 Key。
- 通过手动设置集合类型,可更改解析集合类型,例如:(除了IEnumerable外,其余接口类型均解析出数组类型Array )
注意:集合中解析对象的顺序与注册类型的顺序保持一致。
如何使 Resolve 方法不引发异常
在注册多个类型的前提下,直接使用 Resolve 方法会引发错误。
var commands0 = container.Resolve<ICommand>();
异常信息 :
DryIoc.ContainerException:“code: Error.ExpectedSingleDefaultFactory;
message: Expecting a single default registration but found many:
(DefaultKey(0), {FactoryID=144, ImplType=ConsoleApp1.GetCommand}),
(DefaultKey(1), {FactoryID=145, ImplType=ConsoleApp1.SetCommand}),
(DefaultKey(2), {FactoryID=146, ImplType=ConsoleApp1.DeleteCommand})
when resolving resolution root ConsoleApp1.ICommand
from container without scope.
Please identify service with key, or metadata, or use Rules.WithFactorySelector to specify single registered factory.”
以下几类方法可使得 Resolve 某个特定类型服务实例而不引发异常:
- 使用条件:container.Regsiter
(setup: Setup.With(condition: req => req.IsResolutionRoot )),其他剩下的注册信息则被指定为相反的参数,如: req => !req.IsResolutionRoot。 - 使用元类型(例如 ComandId 枚举)以及解析为 Meta<,>包装器:container.Register
(setup: Setup.With(metadata: CommandId.Get));解析时如下:container.Resolve >>().Where(m => (m.Metadata==Command.Get))。 - 使用特定的作用域
- 注册时携带 Key
携带Key注册方式
在注册时可通过提供 Service Key来区分不同的实现类。Service Key 可以是任意实现了 Object.GetHashCode 和 Object.Equals 的类型。通常使用枚举,字符串或者数字。
标记“服务类”:serviceKey参数
IRegistrator.sRegister 方法中,serviceKey 参数用于在 DryIoc 中唯一标识“服务类”:
public interface IRegistrator
{
//..
void Register(Factory factory, Type serviceType, object serviceKey, IfAlreadyRegistered? ifAlreadyRegistered, bool isStaticallyChecked);
}
从 Container 源码中可知,在serviceKey 为空时,使用 Rules.DefaultRegistrationServiceKey 属性作为 serviceKey 值:
public partial class Container : IContainer
{
public void Register(Factory factory, Type serviceType, object serviceKey, IfAlreadyRegistered? ifAlreadyRegistered, bool isStaticallyChecked)
{
//..
if (serviceKey == null)serviceKey = Rules.DefaultRegistrationServiceKey;
}
//..
}
DryIoc 默认情况下使用 DefaultKey 类作为 serviceKey 参数:
- DefaultKey 类中的 RegistrationOrder 属性(int),主要用于对“实现类”排序。
- DefaultKey 类覆写了 Object.GetHasCode 和 Object.Equals 方法。 ```csharp // DryIco源码:src/DryIoc/Container.cs
///
/// <summary>Compares keys based on registration order. The null (represents default) key is considered equal.</summary>
public override bool Equals(object key) => key == null || (key as DefaultKey)?.RegistrationOrder == RegistrationOrder;
/// <summary>Returns registration order as hash.</summary>
public override int GetHashCode() => RegistrationOrder;
// onther Code
// .....
}
<a name="1o8zQ"></a>
#### 示例 :注册时提供 Service Key
设置 serviceKey 参数手动标记“实现类”
- Register 方法的 serviceKey 参数接受一个 object 类型对象,该对象需要实现 Object.GetHasCode 和 Object.Equals 方法,通常该参数会使用**枚举,字符串或者数值**。
- 未提供 serviceKey 参数时,默认使用了 DefaultKey 类型对象,该类覆写了 Object.GetHasCode 和 Object.Equals 方法。
```csharp
class Multiple_keyed_registrations
{
internal interface ICommand { }
internal class GetCommand :唯一标记 ICommand { }
internal class SetCommand : ICommand { }
internal class DeleteCommand : ICommand { }
enum CommandId { Get, Set, Delete }
[Test]
public void Example()
{
var container = new Container();
container.Register<ICommand, GetCommand>(serviceKey: CommandId.Get);
container.Register<ICommand, SetCommand>(serviceKey: CommandId.Set);
container.Register<ICommand, DeleteCommand>(serviceKey: CommandId.Delete);
// 这里通过 serviceKey 指定解析的类型
var setCommand = container.Resolve<ICommand>(serviceKey: CommandId.Set);
Assert.IsInstanceOf<SetCommand>(setCommand);
// 解析所有类型到结合中
var commands = container.Resolve<ICommand[]>();
Assert.AreEqual(3, commands.Length);
// 这里通过 Behaviro 将解析集合类型设为固定长度的数组
var commands2 = (ICommand[])container.ResolveMany<ICommand>(behavior: ResolveManyBehavior.AsFixedArray);
Assert.AreEqual(3, commands2.Length);
}
}
还有一种方法用于读取各自的 Key。它与 Lazy 或者 Func 包装器结合起来特别好用,尤其是针对菜单或导航这类UI需求。
示例 :获取注册信息的 Service Key
class Resolve_commands_with_keys
{
internal interface ICommand { }
internal class GetCommand : ICommand { }
internal class SetCommand : ICommand { }
internal class DeleteCommand : ICommand { }
enum CommandId { Get, Set, Delete }
[Test]
public void Example()
{
var container = new Container();
container.Register<ICommand, GetCommand>(serviceKey: CommandId.Get);
container.Register<ICommand, SetCommand>(serviceKey: CommandId.Set);
container.Register<ICommand, DeleteCommand>(serviceKey: CommandId.Delete);
var commands = container.Resolve<KeyValuePair<CommandId, ICommand>[]>();
Assert.AreEqual(CommandId.Get, commands[0].Key);
// or as Lazy
var lazyCommands = container.Resolve<KeyValuePair<CommandId, Lazy<ICommand>>[]>();
Assert.AreEqual(CommandId.Set, lazyCommands[1].Key);
// or as Func
var commandFactories = container.Resolve<KeyValuePair<CommandId, Func<ICommand>>[]>();
Assert.AreEqual(CommandId.Delete, commandFactories[2].Key);
}
}
解析 KeyValuePair 包装器
使用 KeyValuePair 作为解析对象的包装类型,可获取注册信息的 serviceKey
- 使用 KeyValuePair 作为包装类型解析的对象时,Key 属性对应的是 serviceKey,而 Value 属性对应的真正解析对象。
- DryIoc并不能保证返回非 DefaultKey 类型对象的注册顺序,即:对于手动设置 serviceKey 的多个类型之间排序顺序是不能得到保证。
注意:解析集合类型过程中,若多个注册信息使用的 service Key并非是同一类型,那么在 Pair 中需要指定 object 类型作为 TKey。
示例 :通过解析 KeyValuePair 获取 serviceKey
class Resolving_service_with_key_as_KeyValuePair
{
interface I { }
class X : I { }
class Y : I { }
class Z : I { }
[Test]
public void Example()
{
var container = new Container();
container.Register<I, X>();
container.Register<I, Y>();
container.Register<I, Z>(serviceKey: "z");
// 这里将 object 作为 KeyValuePair 的 Tkey
var items = container.Resolve<KeyValuePair<object, I>[]>();
CollectionAssert.AreEqual(
new object[] { DefaultKey.Of(0), DefaultKey.Of(1), "z" },
items.Select(x => x.Key));
// 这里将 DefualtKey 作为 KeyValuePair 的 Tkey
var defaultItems = container.Resolve<KeyValuePair<DefaultKey, I>[]>();
Assert.AreEqual(2, defaultItems.Length);
// 这里将 string 作为 KeyValuePair 的 Tkey
var z = container.Resolve<KeyValuePair<string, I>[]>().Single().Value;
Assert.IsInstanceOf<Z>(z);
}
}
上述例子可以看出,serviceKey 的类型可以起到过滤作用。
注意:DryIoc不保证非默认Key注册类型的解析顺序。因此,在上面的示例中,“z”注册不能保证是最后一个,即使它是最后一个注册的。
IsRegistered 方法
IsRegistered 方法允许你检测某特定类型、装饰器或者包装器是否已在容器中注册。
另外,IsRegistered 方法允许通过提供 condition 去检测注册类型的 Factory。例如,可以检测具体的 Reuse,Metadata 或者 Setup。
示例 :IsRegistered 利用 condition 检测某类型是否以“单例模式”注册
class IsRegistered_examples
{
class MyService { }
[Test]
public void Example()
{
var c = new Container();
// not registered yet
Assert.IsFalse(c.IsRegistered<MyService>(condition: factory => factory.Reuse is SingletonReuse));
c.Register<MyService>();
// Assert通过,此时 MyService 类型已注册
Assert.IsTrue(c.IsRegistered<MyService>());
// Assert通过,这里使用 condition 将 MyService 限定为单例模式作为条件,结果条件不满足
Assert.IsFalse(c.IsRegistered<MyService>(condition: factory => factory.Reuse is SingletonReuse));
c.Register<MyService>(Reuse.Singleton);
// Assert通过,此时 MyService 类型已注册为单例模式
Assert.IsTrue(c.IsRegistered<MyService>(condition: factory => factory.Reuse is SingletonReuse));
}
}
IsRegistered 方法源码:
- condition 参数类型为 Func
,基于 Factory 参数返回 bool 类型用作于过滤条件 - factoryType 参数 默认为 FactoryType.Service,表明该方法默认情况用于判断“服务类” ```csharp // DryIco源码:src/DryIoc/Container.cs
///
IsRegistered 方法支持传入 serviceKey 用于作为查找条件。
<a name="3PY4m"></a>
#### 示例 :IsRegistered 利用 serviceKey 查找指定Key的注册信息
```csharp
class IsRegistered_with_key_examples
{
class MyService { }
[Test]
public void Example()
{
var c = new Container();
c.Register<MyService>(serviceKey: "the key");
Assert.IsTrue(c.IsRegistered<MyService>(serviceKey: "the key"));
// 注册时提供了字符串作为 sercieKey,因此 DefaultKey.Value 不满足查找条件
Assert.IsFalse(c.IsRegistered<MyService>(serviceKey: DefaultKey.Value));
// 缺少 serviceKey 参数时,会查找所有的注册类型
Assert.IsTrue(c.IsRegistered<MyService>());
c.Register<MyService>();
// 针对未提供 sercieKey的注册信息,可使用 DefaultKey.Value 作为参数
Assert.IsTrue(c.IsRegistered<MyService>(serviceKey: DefaultKey.Value));
}
}
默认情况下,IsRegistered 仅会检测“服务类”,忽略“装饰器”或者“包装器”。通过 factoryType 可以指定查找的目标类型。
手动修改 IsRegister 方法的 factoryType 参数,可用于判断“装饰类”或“包装类”,例如:
示例 :IsRegistered 利用 factoryType 参数用查找“包装器”
class IsRegistered_for_wrapper_or_decorators
{
class Owned<T> { }
[Test]
public void Example()
{
var c = new Container();
Assert.IsFalse(c.IsRegistered(typeof(Owned<>), factoryType: FactoryType.Wrapper));
c.Register(typeof(Owned<>), setup: Setup.Wrapper);
Assert.IsTrue(c.IsRegistered(typeof(Owned<>), factoryType: FactoryType.Wrapper));
}
}
重要 :IsRegistered 方法并不会检查“服务类”是否能真的解析。例如:它的某些依赖对象并没有注册。通过 Resolve 方法的 IfUnresolved.ReturnDefault 参数可以解决该问题:
示例 :Resolve 方法使用 IfUnresolved.ReturnDefault 检测是否解析
class Check_if_resolvable
{
class MyService { }
[Test]
public void Example()
{
var c = new Container();
// Assert通过,因未注册MyService类型,此时无法正常解析,根据参数IfUnresolved.ReturnDefault,返回引用类型的默认值(null)
Assert.IsFalse(c.Resolve<MyService>(ifUnresolved: IfUnresolved.ReturnDefault) != null);
c.Register<MyService>();
// Assert通过,已注册MyService类型,此时能正常解析MyService实例
Assert.IsTrue(c.Resolve<MyService>(ifUnresolved: IfUnresolved.ReturnDefault) != null);
// 利用 Func 包装方式,可以在不创建具体实例的情况下检测类型的解析性
// Assert通过,已注册MyService类型,此时能正常解析 Func<MyService> 方法
Assert.IsTrue(c.Resolve<Func<MyService>>(ifUnresolved: IfUnresolved.ReturnDefault) != null);
}
}
以隐式方式获知“服务类”是否已注册
GetSercieRegistrations 方法用于获取容器中所有的服务类注册信息。
示例 :使用 GetSercieRegistrations 方法获取所有服务类注册信息
class Get_specific_registration
{
class MyService { }
[Test]
public void Example()
{
var c = new Container();
c.Register<MyService>(Reuse.Scoped, serviceKey: "foo");
// 这里通过 GetSericeRegistration 方法获取所有的注册信息
var serviceRegistration = c.GetServiceRegistrations()
.FirstOrDefault(r => Equals(r.OptionalServiceKey, "foo") && r.Factory.Reuse == Reuse.Scoped);
Assert.AreEqual(typeof(MyService), serviceRegistration.ImplementationType);
}
}
RegisterMany 方法
Batch registering the implementations with possibly many service types,throwing the “Error.NoServicesWereRegisteredByRegisterMany” error when there are no services types to register.
RegisterMany 方法允许批量注册。此外,它会自动从“实现类”(或者程序集中实现类)推论出“服务类”。
使用 RegisterMany 方法可同时为多个类型实现注册
示例 :使用 RegisterMany 方法注册多个类型
public interface X { }
public interface Y { }
public class A : X, Y { }
public class B : X, IDisposable
{
public void Dispose() { }
}
public static A CreateA() => new A();
// 利用 rules 参数设置 WithTrackingDisposableTransients,启用瞬时对象跟踪功能
var container = new Container(rules => rules.WithTrackingDisposableTransients());
- 注册单个“实现类” ```csharp // 为 X,Y以及A本身, 同时注册“实现类” A container.RegisterMany();
// 使用 serviceTypeConditon 参数,设置过滤条件:只为接口(X,Y)注册“实现类”A container.RegisterMany(serviceTypeCondition: type => type.IsInterface);
// 使用 reuse 参数,设置为单例模式
container.RegisterMany(Reuse.Singleton);
Assert.AreSame(container.Resolve
- 同时注册多个“实现类”
```csharp
// 同时注册 A 与 B,利用serviceTypeCondition参数,设置过滤条件:只为接口类型注册,即 X、Y 注册 A,X 注册 B
// 此时 Resolve<X>() 对象会抛出错误,因为 X 同时注册了 A 与 B
container.RegisterMany(new[] { typeof(A), typeof(B) },serviceTypeCondition: type => type.IsInterface);
// 同时注册 A 与 B,利用serviceTypeCondition参数,设置过滤条件:只为 X 注册 A 与 B
// 此时 Resolve<X>() 对象会抛出错误,因为 X 同时注册了 A 与 B
container.RegisterMany(new[] { typeof(A), typeof(B) },serviceTypeCondition: type => type == typeof(X));
// 注册 A 所在程序集中的其他类型,利用serviceTypeCondition参数,设置过滤条件:只为 X 注册 A 与 B
container.RegisterMany(new[] { typeof(A).Assembly }, type => type == typeof(X));
- 使用 Made.Of 方法 ```csharp // 利用 Made.Of 方法配合 Func 方法注册 A container.RegisterMany(Made.Of(() => CreateA()));
// DryIco源码:src/DryIoc/Container.cs
public class Made
{
public static TypedMade
- “显式”注册
```csharp
// 为 X,Y 注册 A
container.RegisterMany(new[] { typeof(X), typeof(Y) }, typeof(A));
// 抛出异常:A 并未注册为“服务类”
container.Resolve<A>();
- 自定义注册参数
container.RegisterMany(new[] { typeof(A).Assembly },
getServiceTypes: implType => implType.GetImplementedServiceTypes(),
getImplFactory: implType => new ReflectionFactory(implType,
reuse: implType.IsAssignableTo<IDisposable>() ? Reuse.Scoped : Reuse.Transient,
made: FactoryMethod.ConstructorWithResolvableArguments));
DryIoc 不会将某些类型视为 RegisterMany 方法的服务类型。这些类型包括 .Net基元类型、object、string以及某些通常接口(例如:IDisposable,ICloneable)。编译器生成的类型同样排除在外。
关于Registrator.ExcludedGeneralPurposeServiceTypes
- 通过 Registrator.ExcludedGeneralPurposeServiceTypes 属性获取当前框架下 RegisterMany 方法不支持的 Type 集合
- 针对 RegisterMany 不支持的 Type,可以使用 Register 方法。
var excludedTypes = Registrator.ExcludedGeneralPurposeServiceTypes;
RegisterMapping 方法
Registers new service type with factory for registered service type.
RegisterMapping 方法允许为已存在注册信息新增一个新注册。
示例 :RegisterMapping 方法基本使用
class Register_mapping
{
public interface I { }
public interface J { }
class S : I, J { } // implements both I and J
[Test]
public void Example()
{
var container = new Container();
// 为 I 注册 S (单例模式)
container.Register<I, S>(Reuse.Singleton);
// 为 J 注册匹配 I,即相当于为 J 注册 S (单例)
container.RegisterMapping<J, I>();
Assert.AreSame(container.Resolve<I>(), container.Resolve<J>());
}
}
示例 :RegisterMany 方法实现 RegisterMapping 方法的效果
class Register_mapping_with_RegisterMany
{
public interface I { }
public interface J { }
class S : I, J { } // implements both I and J
[Test]
public void Example()
{
var container = new Container();
container.RegisterMany(new[] { typeof(I), typeof(J) }, typeof(S), Reuse.Singleton);
Assert.AreSame(container.Resolve<I>(), container.Resolve<J>());
Assert.AreSame(container.Resolve<I>(), container.Resolve<J>());
}
}