diff --git a/VContainer/Assets/Tests/ContainerTest.cs b/VContainer/Assets/Tests/ContainerTest.cs index ad8247d5..635d6618 100644 --- a/VContainer/Assets/Tests/ContainerTest.cs +++ b/VContainer/Assets/Tests/ContainerTest.cs @@ -328,7 +328,7 @@ public void RegisterFromFunc() Assert.That(resolved, Is.InstanceOf()); Assert.That(resolved.Service2, Is.InstanceOf()); } - + [Test] public void RegisterValueTypeFromFunc() { @@ -442,7 +442,7 @@ public void RegisterWithParameter() var resolved = container.Resolve(); Assert.That(resolved.Service2, Is.EqualTo(paramValue)); } - + { var builder = new ContainerBuilder(); builder.Register(Lifetime.Scoped); @@ -601,8 +601,10 @@ public void OnContainerDisposeCallback() builder.Register(Lifetime.Scoped); builder.Register(Lifetime.Scoped); - builder.RegisterDisposeCallback(resolver => resolvedJustBeforeDispose = resolver.Resolve()); - builder.RegisterDisposeCallback(resolver => resolvedJustBeforeDispose2 = resolver.Resolve()); + builder.RegisterDisposeCallback(resolver => + resolvedJustBeforeDispose = resolver.Resolve()); + builder.RegisterDisposeCallback(resolver => + resolvedJustBeforeDispose2 = resolver.Resolve()); var container = builder.Build(); @@ -614,5 +616,61 @@ public void OnContainerDisposeCallback() Assert.That(resolvedJustBeforeDispose, Is.Not.Null); Assert.That(resolvedJustBeforeDispose2, Is.Not.Null); } + + [Test] + public void TryResolveTransient() + { + var builder = new ContainerBuilder(); + builder.Register(Lifetime.Transient); + + var container = builder.Build(); + + Assert.That(container.TryResolve(out var obj1), Is.True); + Assert.That(container.TryResolve(out var obj2), Is.True); + Assert.That(container.TryResolve(out var obj3), Is.False); + + Assert.That(obj1, Is.TypeOf()); + Assert.That(obj2, Is.TypeOf()); + Assert.That(obj1, Is.Not.EqualTo(obj2)); + Assert.That(obj3, Is.Null); + } + + [Test] + public void TryResolveSingleton() + { + var builder = new ContainerBuilder(); + builder.Register(Lifetime.Singleton); + + var container = builder.Build(); + Assert.That(container.TryResolve(out var obj1), Is.True); + Assert.That(container.TryResolve(out var obj2), Is.True); + Assert.That(container.TryResolve(out var obj3), Is.False); + + Assert.That(obj1, Is.TypeOf()); + Assert.That(obj2, Is.TypeOf()); + Assert.That(obj1, Is.EqualTo(obj2)); + Assert.That(obj3, Is.Null); + } + + [Test] + public void TryResolveScoped() + { + var builder = new ContainerBuilder(); + builder.Register(Lifetime.Scoped); + + var container = builder.Build(); + Assert.That(container.TryResolve(out var obj1), Is.True); + Assert.That(container.TryResolve(out var obj2), Is.True); + Assert.That(container.TryResolve(out var obj3), Is.False); + + Assert.That(obj1, Is.TypeOf()); + Assert.That(obj2, Is.TypeOf()); + Assert.That(obj1, Is.EqualTo(obj2)); + Assert.That(obj3, Is.Null); + + container.Dispose(); + + Assert.That(obj1.Disposed, Is.True); + } } } diff --git a/VContainer/Assets/VContainer/Runtime/Container.cs b/VContainer/Assets/VContainer/Runtime/Container.cs index 639310e3..e7d0b925 100644 --- a/VContainer/Assets/VContainer/Runtime/Container.cs +++ b/VContainer/Assets/VContainer/Runtime/Container.cs @@ -19,6 +19,15 @@ public interface IObjectResolver : IDisposable /// object Resolve(Type type); + /// + /// Try resolve from type + /// + /// + /// This version of resolve looks for all of scopes + /// + /// Successfully resolved + bool TryResolve(Type type, out object resolved); + /// /// Resolve from meta with registration /// @@ -26,7 +35,9 @@ public interface IObjectResolver : IDisposable /// This version of resolve will look for instances from only the registration information already founds. /// object Resolve(Registration registration); + IScopedObjectResolver CreateScope(Action installation = null); + void Inject(object instance); bool TryGetRegistration(Type type, out Registration registration); } @@ -73,7 +84,26 @@ internal ScopedContainer( } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public object Resolve(Type type) => Resolve(FindRegistration(type)); + public object Resolve(Type type) + { + if (TryFindRegistration(type, out var registration)) + { + return Resolve(registration); + } + throw new VContainerException(type, $"No such registration of type: {type}"); + } + + public bool TryResolve(Type type, out object resolved) + { + if (TryFindRegistration(type, out var registration)) + { + resolved = Resolve(registration); + return true; + } + + resolved = default; + return false; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public object Resolve(Registration registration) @@ -154,18 +184,20 @@ object CreateTrackedInstance(Registration registration) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Registration FindRegistration(Type type) + internal bool TryFindRegistration(Type type, out Registration registration) { IScopedObjectResolver scope = this; while (scope != null) { - if (scope.TryGetRegistration(type, out var registration)) + if (scope.TryGetRegistration(type, out registration)) { - return registration; + return true; } scope = scope.Parent; } - throw new VContainerException(type, $"No such registration of type: {type}"); + + registration = default; + return false; } } @@ -196,13 +228,26 @@ internal Container(Registry registry, object applicationOrigin = null) [MethodImpl(MethodImplOptions.AggressiveInlining)] public object Resolve(Type type) { - if (registry.TryGet(type, out var registration)) + if (TryGetRegistration(type, out var registration)) { return Resolve(registration); } throw new VContainerException(type, $"No such registration of type: {type}"); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryResolve(Type type, out object resolved) + { + if (TryGetRegistration(type, out var registration)) + { + resolved = Resolve(registration); + return true; + } + + resolved = default; + return false; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public object Resolve(Registration registration) { diff --git a/VContainer/Assets/VContainer/Runtime/IObjectResolverExtensions.cs b/VContainer/Assets/VContainer/Runtime/IObjectResolverExtensions.cs index cd3b15be..68967d78 100644 --- a/VContainer/Assets/VContainer/Runtime/IObjectResolverExtensions.cs +++ b/VContainer/Assets/VContainer/Runtime/IObjectResolverExtensions.cs @@ -9,6 +9,30 @@ public static class IObjectResolverExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Resolve(this IObjectResolver resolver) => (T)resolver.Resolve(typeof(T)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryResolve(this IObjectResolver resolver, out T resolved) + { + if (resolver.TryResolve(typeof(T), out var r)) + { + resolved = (T)r; + return true; + } + + resolved = default; + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ResolveOrDefault(this IObjectResolver resolver, T defaultValue = default) + { + if (resolver.TryResolve(typeof(T), out var value)) + { + return (T)value; + } + + return defaultValue; + } + // Using from CodeGen [Preserve] [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/VContainer/Assets/VContainer/Runtime/Unity/EntryPointDispatcher.cs b/VContainer/Assets/VContainer/Runtime/Unity/EntryPointDispatcher.cs index e75f6471..c47d91a4 100644 --- a/VContainer/Assets/VContainer/Runtime/Unity/EntryPointDispatcher.cs +++ b/VContainer/Assets/VContainer/Runtime/Unity/EntryPointDispatcher.cs @@ -22,14 +22,7 @@ public void Dispatch() { PlayerLoopHelper.EnsureInitialized(); - EntryPointExceptionHandler exceptionHandler = null; - try - { - exceptionHandler = container.Resolve(); - } - catch (VContainerException ex) when (ex.InvalidType == typeof(EntryPointExceptionHandler)) - { - } + EntryPointExceptionHandler exceptionHandler = container.ResolveOrDefault(); var initializables = container.Resolve>>().Value; for (var i = 0; i < initializables.Count; i++)