DryIoc
中的装饰器一般代表一个装饰器模式
。但是结合其他DryIoc
功能,尤其是与FactoryMethod
结合使用,该概念可能会进一步扩展以涵盖更多场景。
DryIoc 装饰器支持:
- 常用服务装饰,为服务添加相关功能
- 根据条件应用装饰器
- 嵌套装饰器并指定自定义嵌套顺序
- 泛型装饰器支持:泛型变化,约束,泛型工厂方法
- 装饰器可以有与被装饰服务不同的重用方式。这里有个额外的为装饰器定义的选项:
useDecorateeReuse
- 装饰器可以装饰封装在
Func
、Lazy
及其他包装器中的服务 - 联合装饰器的重用方式与工厂方法注册,装饰器可以提供额外的型外
- 使用工厂方式和装饰器条件,可以注册泛型服务的装饰器
1. 常规使用
void Main()
{
var container = new Container();
container.Register<IHandler, FooHandler>();
container.Register<ILogger, InMemoryLogger>();
container.Register<IHandler, LoggingHandlerDecorator>(setup: Setup.Decorator);
// resolved handler will be a decorator instance
var handler = container.Resolve<IHandler>();
Console.WriteLine(handler is LoggingHandlerDecorator);
handler.Handle();
}
public interface IHandler
{
bool IsHandled { get; }
void Handle();
}
public class FooHandler : IHandler
{
public bool IsHandled { get; private set; }
public void Handle()
{
IsHandled = true;
Console.WriteLine(nameof(FooHandler) + ", " + nameof(Handle));
}
}
public interface ILogger
{
void Log(string line);
}
class InMemoryLogger : ILogger
{
public void Log(string line)
{
Console.WriteLine(nameof(InMemoryLogger) + ": " + line);
}
}
public class LoggingHandlerDecorator : IHandler
{
private readonly IHandler _handler;
public readonly ILogger Logger;
public LoggingHandlerDecorator(IHandler handler, ILogger logger)
{
_handler = handler;
Logger = logger;
}
public bool IsHandled => _handler.IsHandled;
public void Handle()
{
Logger.Log("About to handle");
_handler.Handle();
Logger.Log("Successfully handled");
}
}
2. 装饰使用服务键注册的服务
在多数情况下,注册装饰器时仅需要使用setup: Setup.Decorator
参数。像注册正常的服务一样,还有其他的注册选项可供装饰器使用。serviceKey
是不被支持的。原因是,不需要为装饰器指定sreviceKey
,而是需要为指定了serviceKey
的服务注册装饰器。
为了给通过serviceKey
参数注册的服务应用装饰器,需要指定setup
条件或者使用Decortator.Of
方法。
2.1 使用Setup.DecoratorWith
void Main()
{
var container = new Container();
container.Register<IHandler, FooHandler>(serviceKey: "foo");
container.Register<ILogger, InMemoryLogger>();
container.Register<IHandler, LoggingHandlerDecorator>(setup: Setup.DecoratorWith(condition: request => "foo".Equals(request.ServiceKey)));
var handler = container.Resolve<IHandler>("foo");
Console.WriteLine(handler is LoggingHandlerDecorator);
handler.Handle();
}
2.2 使用Setup.DecoratorOf
void Main()
{
var container = new Container();
container.Register<IHandler, FooHandler>(serviceKey: "foo");
container.Register<ILogger, InMemoryLogger>();
container.Register<IHandler, LoggingHandlerDecorator>(setup: Setup.DecoratorOf(decorateeServiceKey: "foo"));
var handler = container.Resolve<IHandler>("foo");
Console.WriteLine(handler is LoggingHandlerDecorator);
handler.Handle();
}
Setup.DecoratorOf
仅仅是DecoratorWith(condition:...)
的封装,以提供更易使用的方式。
void Main()
{
{
var container = new Container();
container.Register<ILogger, InMemoryLogger>();
container.Register<IHandler, FooHandler>(serviceKey: "foo");
container.Register<IHandler, BooHandler>(serviceKey: "boo");
container.Register<IHandler, LoggingHandlerDecorator>(setup: Setup.DecoratorOf<BooHandler>(decorateeServiceKey: "boo"));
var boo = container.Resolve<IHandler>("boo");
boo.Handle();
}
{
var container = new Container();
container.Register<ILogger, InMemoryLogger>();
container.Register<IHandler, FooHandler>();
container.Register<IHandler, BooHandler>(serviceKey: "boo");
container.Register<IHandler, LoggingHandlerDecorator>(setup: Setup.DecoratorOf<BooHandler>());
var boo = container.Resolve<IHandler>("boo");
boo.Handle();
}
}
public interface IHandler
{
bool IsHandled { get; }
void Handle();
}
public class FooHandler : IHandler
{
public bool IsHandled { get; private set; }
public void Handle() {
IsHandled = true;
Console.WriteLine(nameof(FooHandler) + ", " + nameof(Handle));
}
}
class BooHandler : IHandler
{
public bool IsHandled { get; private set; }
public void Handle() {
IsHandled = true;
Console.WriteLine(nameof(BooHandler) + ", " + nameof(Handle));
}
}
interface ILogger { void Log(string line);}
class InMemoryLogger : ILogger {
public void Log(string line) {
Console.WriteLine(nameof(InMemoryLogger) + ": " + line);
}
}
class LoggingHandlerDecorator : IHandler
{
private readonly IHandler _handler;
public readonly ILogger Logger;
public LoggingHandlerDecorator(IHandler handler, ILogger logger) {
_handler = handler;
Logger = logger;
}
public bool IsHandled => _handler.IsHandled;
public void Handle() {
Logger.Log("About to handle");
_handler.Handle();
Logger.Log("Successfully handled");
}
}
3. 嵌套装饰器
第一个注册的装饰器封装服务,后续注册的装饰器封装上一层的装饰器。
void Main()
{
var container = new Container();
container.Register<S>();
container.Register<S, D1>(setup: Setup.Decorator);
container.Register<S, D2>(setup: Setup.Decorator);
var s = container.Resolve<S>();
Console.WriteLine(s is S); // True
Console.WriteLine(s is D1); // False
Console.WriteLine(s is D2); // True
// ACTUALLY, you even can see how service is created yourself
var expr = container.Resolve<LambdaExpression>(typeof(S));
expr.Dump();
}
class S { }
class D1 : S { public D1(S s) { } }
class D2 : S { public D2(S s) { } }
3.1 指定嵌套顺序
void Main()
{
var container = new Container();
container.Register<S>();
container.Register<S, D1>(setup: Setup.DecoratorWith(order: 2));
container.Register<S, D2>(setup: Setup.DecoratorWith(order: 1));
var s = container.Resolve<S>();
Console.WriteLine(s is S); // True
Console.WriteLine(s is D1); // True
Console.WriteLine(s is D2); // False
// ACTUALLY, you even can see how service is created yourself
var expr = container.Resolve<LambdaExpression>(typeof(S));
expr.Dump();
}
class S { }
class D1 : S { public D1(S s) { } }
class D2 : S { public D2(S s) { } }
4. 开放式泛型服务装饰器
void Main()
{
var container = new Container();
container.Register(typeof(S<>));
container.Register(typeof(S<>), typeof(D1<>), setup: Setup.Decorator);
container.Register(typeof(S<>), typeof(D2<>), setup: Setup.Decorator);
// decorator specific to the `S<string>` type
container.Register<S<string>, DStr>(setup: Setup.Decorator);
var intS = container.Resolve<S<int>>();
Console.WriteLine(intS is D2<int>); // True
var strS = container.Resolve<S<string>>();
Console.WriteLine(strS is DStr); // True
}
class S<T> { }
class D1<T> : S<T> { }
class D2<T> : S<T> { }
class DStr : S<string> { }
5. 带类型参数的泛型的装饰器
装饰器可以通过工厂方法进行注册。这带了一个问题:如果工厂方法式开放式泛型且返回一个泛型参数类型时该怎么办?
public interface IStartable
{
void Start();
}
public static class DecoratorFactory
{
public static T Decorate<T>(T service) where T : IStartable
{
service.Start();
return service;
}
}
现在我们将使用Decorate
方法注册应用于任何泛型类型参数T
服务的装饰器。为此,我们需要注册一个object
类型的装饰器,因为T
在其定义的方法之外没有意义。
void Main()
{
var container = new Container();
container.Register<X>();
// register with a service type of `object`
container.Register<object>(
made: Made.Of(req => typeof(DecoratorFactory).SingleMethod(nameof(DecoratorFactory.Decorate)).MakeGenericMethod(req.ServiceType)),
setup: Setup.Decorator);
var x = container.Resolve<X>();
}
public interface IStartable
{
void Start();
}
public static class DecoratorFactory
{
public static T Decorate<T>(T service) where T : IStartable
{
service.Start();
return service;
}
}
public class X : IStartable
{
public bool IsStarted { get; private set; }
public void Start() => IsStarted = true;
}
这个问题是,object
类型的装饰器将被应用于所有的服务,这将影响解析的性能。为了让装饰器更有针对性,可以通过条件加以限制:
void Main()
{
var container = new Container();
container.Register<X>();
var decorateMethod = typeof(DecoratorFactory).SingleMethod(nameof(DecoratorFactory.Decorate));
container.Register<object>(
made: Made.Of(r => decorateMethod.MakeGenericMethod(r.ServiceType)),
setup: Setup.DecoratorWith(r => r.ImplementationType.IsAssignableTo<IStartable>()));
// or we may simplify the condition via `DecoratorOf` method:
container.Register<object>(
made: Made.Of(r => decorateMethod.MakeGenericMethod(r.ServiceType)),
setup: Setup.DecoratorOf<IStartable>());
var x = container.Resolve<X>();
}
public interface IStartable
{
void Start();
}
public static class DecoratorFactory
{
public static T Decorate<T>(T service) where T : IStartable
{
service.Start();
return service;
}
}
public class X : IStartable
{
public bool IsStarted { get; private set; }
public void Start() => IsStarted = true;
}
6. 重用装饰器
装饰器可以像普通服务一样有自己的重用方式。通过这种方式,您可以仅通过应用装饰器向已注册的服务添加基于上下文的重用。
如果没有为装饰器指定重用,则表示该装饰器是默认容器重用 container.Rules.DefaultReuse(默认为 Reuse.Transient)。
void Main()
{
var container = new Container();
container.Register<S>();
// singleton decorator effectively makes the decorated service a singleton too.
container.Register<S, D1>(Reuse.Singleton, setup: Setup.Decorator);
var s1 = container.Resolve<S>();
var s2 = container.Resolve<S>();
Console.WriteLine(s1 == s2); // True
}
class S { }
class D1 : S { public D1(S s) { } }
6.1 使用UseDecorateeReuse参数指定装饰器的重用方式
此设置选项允许装饰器重用方式与装饰的服务相同。当你不希望调整装饰器的重用方式时,该选项是个不错的默认选项。
void Main()
{
var container = new Container();
container.Register<S>(Reuse.Singleton);
container.Register<S, D1>(setup: Setup.DecoratorWith(useDecorateeReuse: true));
var s1 = container.Resolve<S>();
var s2 = container.Resolve<S>();
Console.WriteLine(s1 == s2); // True
}
class S { }
class D1 : S { public D1(S s) { } }
class D2 : S { public D2(S s) { } }
7. 装饰被封装的服务
装饰器可以应用于被封装的服务,以提供惰性、按需创建装饰服务、代理等。
void Main()
{
var container = new Container();
container.Register<ACall>();
container.Register<ACall, LazyCallDecorator>(setup: Setup.Decorator);
var a = container.Resolve<ACall>(); // A is not created.
a.Call(); // A is created and called.
}
class ACall
{
public virtual void Call() { }
}
class LazyCallDecorator : ACall
{
public Lazy<ACall> A;
public LazyCallDecorator(Lazy<ACall> a) { A = a; }
// Creates an object only when calling it.
public override void Call() => A.Value.Call();
}
包装器的装饰器可以像普通服务的装饰器一样嵌套:
void Main()
{
var container = new Container();
container.Register<S>();
container.Register<S, DLazy>(setup: Setup.Decorator);
container.Register<S, DFunc>(setup: Setup.Decorator);
var s = container.Resolve<S>(); // `s` is wrapped in Func with DFunc decorator
s.Dump();
s = ((DFunc)s).S(); // then its wrapped in Lazy with DLazy decorator
s = ((DLazy)s).S.Value; // an actual decorated `S` object
}
class S { }
class DLazy : S
{
public Lazy<S> S { get; }
public DLazy(Lazy<S> s) { S = s; }
}
class DFunc : S
{
public Func<S> S { get; }
public DFunc(Func<S> s) { S = s; }
}
8. 装饰封装器
DryIoc 支持直接装饰包装器以调整相应的包装器行为、添加新功能、应用过滤等。
void Main()
{
var container = new Container();
container.Register<I, A>();
container.Register<I, B>();
container.Register<I, C>(serviceKey: "test");
// by default collection will contain instances of all registered types
var iis = container.Resolve<IEnumerable<I>>();
iis.Dump();
}
public interface I { }
public class A : I { }
public class B : I { }
public class C : I { }
为了排除以serviceKey
指定的服务C
,我们可以为IEnumerable<I>
定义装饰器,它接受所有实例并过滤掉键控的东西:
void Main()
{
var container = new Container();
container.Register<I, A>();
container.Register<I, B>();
container.Register<I, C>(serviceKey: "test");
container.Register<IEnumerable<I>>(
Made.Of(() => GetNoneKeyed(Arg.Of<IEnumerable<KeyValuePair<object, I>>>())),
setup: Setup.Decorator);
var iis = container.Resolve<IEnumerable<I>>();
iis.Dump();
}
static IEnumerable<I> GetNoneKeyed(IEnumerable<KeyValuePair<object, I>> all) => all.Where(kv => kv.Key is DefaultKey).Select(kv => kv.Value);
public interface I { }
public class A : I { }
public class B : I { }
public class C : I { }
9. 装饰器作为初始化器
使用 FactoryMethod 注册装饰器时,可以装饰服务的初始化管道。这意味着装饰器工厂方法接受注入的装饰服务,在被装饰的实例上调用一些初始化逻辑,并返回这个(或另一个)实例。
void Main()
{
var container = new Container();
container.Register<Foo>();
container.Register<Foo>(Made.Of(() => DecorateFooWithGreet(Arg.Of<Foo>())), setup: Setup.Decorator);
var foo = container.Resolve<Foo>();
Console.WriteLine(foo.Message);
}
public class Foo
{
public string Message { get; set; }
}
// a static method just for the demonstration
public static Foo DecorateFooWithGreet(Foo foo)
{
foo.Message = "Hello, " + (foo.Message ?? "");
return foo;
}