diff --git a/Assets/Reflex.Tests/TransientValueResolverTests.cs b/Assets/Reflex.Tests/TransientValueResolverTests.cs new file mode 100644 index 00000000..4001e836 --- /dev/null +++ b/Assets/Reflex.Tests/TransientValueResolverTests.cs @@ -0,0 +1,46 @@ +using System; +using FluentAssertions; +using NUnit.Framework; +using Reflex.Core; + +namespace Reflex.Tests +{ + public class TransientValueResolverTests + { + [Test] + public void AddTransientFromValue_FirstResolve_ShouldReturnValue() + { + var container = new ContainerDescriptor("") + .AddTransient(42) + .Build(); + + container.Resolve().Should().Be(42); + } + + [Test] + public void AddValueType_AsTransientFromValue_SecondResolve_ShouldThrow() + { + var container = new ContainerDescriptor("") + .AddTransient(42) + .Build(); + + Action resolve = () => container.Resolve(); + + resolve.Should().NotThrow(); + resolve.Should().Throw(); + } + + [Test] + public void AddReferenceType_AsTransientFromValue_SecondResolve_ShouldThrow() + { + var container = new ContainerDescriptor("") + .AddTransient(string.Empty) + .Build(); + + Action resolve = () => container.Resolve(); + + resolve.Should().NotThrow(); + resolve.Should().Throw(); + } + } +} \ No newline at end of file diff --git a/Assets/Reflex.Tests/TransientValueResolverTests.cs.meta b/Assets/Reflex.Tests/TransientValueResolverTests.cs.meta new file mode 100644 index 00000000..de445c8f --- /dev/null +++ b/Assets/Reflex.Tests/TransientValueResolverTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a7e00afb3082403e95a0f491e80679b6 +timeCreated: 1701703662 \ No newline at end of file diff --git a/Assets/Reflex/Core/Container.cs b/Assets/Reflex/Core/Container.cs index db75654c..103f4a2c 100644 --- a/Assets/Reflex/Core/Container.cs +++ b/Assets/Reflex/Core/Container.cs @@ -77,7 +77,10 @@ public object Resolve(Type type) return All(elementType).CastDynamic(elementType); } - return GetResolvers(type).Last().Resolve(this); + var resolvers = GetResolvers(type); + var lastResolver = resolvers.Last(); + var resolved = lastResolver.Resolve(this); + return resolved; } public TContract Resolve() diff --git a/Assets/Reflex/Core/ContainerDescriptor.cs b/Assets/Reflex/Core/ContainerDescriptor.cs index f3e3d418..67a3522e 100644 --- a/Assets/Reflex/Core/ContainerDescriptor.cs +++ b/Assets/Reflex/Core/ContainerDescriptor.cs @@ -44,6 +44,26 @@ public Container Build() OnContainerBuilt?.Invoke(container); return container; } + + public ContainerDescriptor AddType(Type concrete, Type[] contracts, Lifetime lifetime) + { + switch (lifetime) + { + case Lifetime.Singleton: return Add(concrete, contracts, new SingletonTypeResolver(concrete)); + case Lifetime.Transient: return Add(concrete, contracts, new TransientTypeResolver(concrete)); + default: throw new ArgumentOutOfRangeException(nameof(lifetime), lifetime, null); + } + } + + public ContainerDescriptor AddValue(Type concrete, Type[] contracts, Lifetime lifetime) + { + switch (lifetime) + { + case Lifetime.Singleton: return Add(concrete, contracts, new SingletonTypeResolver(concrete)); + case Lifetime.Transient: return Add(concrete, contracts, new TransientTypeResolver(concrete)); + default: throw new ArgumentOutOfRangeException(nameof(lifetime), lifetime, null); + } + } public ContainerDescriptor AddSingleton(Type concrete, params Type[] contracts) { @@ -90,6 +110,16 @@ public ContainerDescriptor AddTransient(Type concrete) { return AddTransient(concrete, concrete); } + + public ContainerDescriptor AddTransient(object instance, params Type[] contracts) + { + return Add(instance.GetType(), contracts, new TransientValueResolver(instance)); + } + + public ContainerDescriptor AddTransient(object instance) + { + return AddTransient(instance, instance.GetType()); + } public ContainerDescriptor AddTransient(Func factory, params Type[] contracts) { diff --git a/Assets/Reflex/Resolvers/TransientValueResolver.cs b/Assets/Reflex/Resolvers/TransientValueResolver.cs new file mode 100644 index 00000000..47fffb3f --- /dev/null +++ b/Assets/Reflex/Resolvers/TransientValueResolver.cs @@ -0,0 +1,34 @@ +using System; +using Reflex.Core; +using Reflex.Enums; + +namespace Reflex.Resolvers +{ + internal sealed class TransientValueResolver : Resolver + { + private object _value; + + public TransientValueResolver(object value) + { + RegisterCallSite(); + _value = value; + Disposables.TryAdd(value); + Concrete = _value.GetType(); + Lifetime = Lifetime.Transient; + } + + public override object Resolve(Container container) + { + IncrementResolutions(); + + if (_value == null) + { + throw new Exception("Trying to resolve a second time from a TransientValueResolver"); + } + + var value = _value; + _value = null; + return value; + } + } +} \ No newline at end of file diff --git a/Assets/Reflex/Resolvers/TransientValueResolver.cs.meta b/Assets/Reflex/Resolvers/TransientValueResolver.cs.meta new file mode 100644 index 00000000..9767e488 --- /dev/null +++ b/Assets/Reflex/Resolvers/TransientValueResolver.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2e36428882a34c828a539b006966fc26 +timeCreated: 1701703560 \ No newline at end of file diff --git a/README.md b/README.md index d9b9f329..6e8feb15 100644 --- a/README.md +++ b/README.md @@ -252,6 +252,15 @@ If object implements `IDisposable` it will be disposed when its parent Container Theres no need to pass `IDisposable` as contract to have your object disposed, howerver, if you want to retrieve all `IDisposable` by any API `Single`, `Resolve` or `All` then yes, you have to specify it. > Note that `IStartable` also works for **Transients** but pay attention that any resolve API will create a new instance +### AddTransient (From Value) +```csharp +ContainerDescriptor::AddTransient(object instance, params Type[] contracts) +``` +Adds an object already contructed by the user to the container as a transient. +Its gonna be returned only on first time it gets resolved, second time an exception will be throw. +If object implements `IDisposable` it will be disposed when its parent Container are disposed. +Theres no need to pass `IDisposable` as contract to have your object disposed, howerver, if you want to retrieve all `IDisposable` by any API `Single`, `Resolve` or `All` then yes, you have to specify it. + ### AddTransient (From Factory) ```csharp ContainerDescriptor::AddTransient(Func factory, params Type[] contracts)