diff --git a/VContainer/Assets/Tests/DecoratorTest.cs b/VContainer/Assets/Tests/DecoratorTest.cs new file mode 100644 index 00000000..047faae8 --- /dev/null +++ b/VContainer/Assets/Tests/DecoratorTest.cs @@ -0,0 +1,49 @@ +using NUnit.Framework; +using VContainer.Runtime; + +namespace VContainer.Tests +{ + interface IDecoratedType + { + } + + class DecoratedType : IDecoratedType + { + } + + class Decorator1 : IDecoratedType + { + readonly IDecoratedType inner; + + public Decorator1(IDecoratedType inner) + { + this.inner = inner; + } + } + + class Decorator2 : IDecoratedType + { + readonly IDecoratedType inner; + + public Decorator2(IDecoratedType inner) + { + this.inner = inner; + } + } + + [TestFixture] + public class DecoratorTest + { + [Test] + public void Decorate() + { + var builder = new ContainerBuilder(); + builder.Register(Lifetime.Singleton); + builder.RegisterDecorator(); + + var container = builder.Build(); + var instance = container.Resolve(); + Assert.That(instance, Is.TypeOf()); + } + } +} diff --git a/VContainer/Assets/Tests/DecoratorTest.cs.meta b/VContainer/Assets/Tests/DecoratorTest.cs.meta new file mode 100644 index 00000000..e51da624 --- /dev/null +++ b/VContainer/Assets/Tests/DecoratorTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c8305f2f09df4b9faa77d32797c77541 +timeCreated: 1706670798 \ No newline at end of file diff --git a/VContainer/Assets/VContainer/Runtime/DecoratorRegistrationBuilder.cs b/VContainer/Assets/VContainer/Runtime/DecoratorRegistrationBuilder.cs new file mode 100644 index 00000000..dc70a7af --- /dev/null +++ b/VContainer/Assets/VContainer/Runtime/DecoratorRegistrationBuilder.cs @@ -0,0 +1,91 @@ +using System; +using VContainer.Internal; + +namespace VContainer.Runtime +{ + public static class ConatinerBuilderDecoratorExtensions + { + public static DecoratorRegistrationBuilder RegisterDecorator(this IContainerBuilder builder) + { + for (var i = 0; i < builder.Count; i++) + { + var interfaceTypes = builder[i].InterfaceTypes; + if (interfaceTypes != null && interfaceTypes.Contains(typeof(TInterface))) + { + return new DecoratorRegistrationBuilder(builder[i], typeof(TDecorator)); + } + } + throw new VContainerException(typeof(TInterface), $"No such decorator target: {typeof(TInterface)}"); + } + + public static DecoratorRegistrationBuilder RegisterDecorator( + this IContainerBuilder builder, + Func factory) + { + for (var i = 0; i < builder.Count; i++) + { + var interfaceTypes = builder[i].InterfaceTypes; + if (interfaceTypes != null && interfaceTypes.Contains(typeof(TInterface))) + { + return new FuncDecoratorRegistrationBuilder(builder[i], factory); + } + } + throw new VContainerException(typeof(TInterface), $"No such decorator target: {typeof(TInterface)}"); + } + } + + public class DecoratorRegistrationBuilder : RegistrationBuilder + { + readonly RegistrationBuilder inner; + readonly Type interfaceType; + + public DecoratorRegistrationBuilder(RegistrationBuilder inner, Type decoratorType) + : base(decoratorType, inner.Lifetime) + { + this.inner = inner; + interfaceType = inner.InterfaceTypes != null ? inner.InterfaceTypes[0] : inner.ImplementationType; + InterfaceTypes = inner.InterfaceTypes; + As(interfaceType); + } + + public override Registration Build() + { + var injector = InjectorCache.GetOrBuild(ImplementationType); + var innerRegistration = inner.Build(); + + var provider = new FuncInstanceProvider(container => + { + var innerInstance = container.Resolve(innerRegistration); + var parameters = new IInjectParameter[Parameters == null ? 1 : Parameters.Count]; + Parameters?.CopyTo(parameters); + parameters[parameters.Length - 1] = new TypedParameter(interfaceType, innerInstance); + return injector.CreateInstance(container, parameters); + }); + return new Registration(ImplementationType, Lifetime, InterfaceTypes, provider); + } + } + + public class FuncDecoratorRegistrationBuilder : DecoratorRegistrationBuilder + { + readonly RegistrationBuilder inner; + readonly Type interfaceType; + readonly Func factory; + + public FuncDecoratorRegistrationBuilder(RegistrationBuilder inner, Func factory) + : base(inner, typeof(TDecorator)) + { + this.factory = factory; + } + + public override Registration Build() + { + var innerRegistration = inner.Build(); + var provider = new FuncInstanceProvider(container => + { + var innerInstance = container.Resolve(innerRegistration); + return factory((TInner)innerInstance, container); + }); + return new Registration(ImplementationType, Lifetime, InterfaceTypes, provider); + } + } +} diff --git a/VContainer/Assets/VContainer/Runtime/DecoratorRegistrationBuilder.cs.meta b/VContainer/Assets/VContainer/Runtime/DecoratorRegistrationBuilder.cs.meta new file mode 100644 index 00000000..18b60ec0 --- /dev/null +++ b/VContainer/Assets/VContainer/Runtime/DecoratorRegistrationBuilder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7250d5904522480090fbd0013774fea9 +timeCreated: 1706669727 \ No newline at end of file diff --git a/VContainer/Assets/VContainer/Runtime/RegistrationBuilder.cs b/VContainer/Assets/VContainer/Runtime/RegistrationBuilder.cs index ace303b2..a1784fc8 100644 --- a/VContainer/Assets/VContainer/Runtime/RegistrationBuilder.cs +++ b/VContainer/Assets/VContainer/Runtime/RegistrationBuilder.cs @@ -10,7 +10,7 @@ public class RegistrationBuilder protected internal readonly Lifetime Lifetime; protected internal List InterfaceTypes; - protected internal List Parameters; + protected List Parameters; public RegistrationBuilder(Type implementationType, Lifetime lifetime) {