Skip to content

Commit

Permalink
Merge pull request #656 from hadashiA/TryResolve
Browse files Browse the repository at this point in the history
Port: Added a TryResolve method to IObjectResolver #585
  • Loading branch information
hadashiA authored Apr 4, 2024
2 parents f2afd2a + 921ec3d commit b4a5514
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 18 deletions.
66 changes: 62 additions & 4 deletions VContainer/Assets/Tests/ContainerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ public void RegisterFromFunc()
Assert.That(resolved, Is.InstanceOf<ServiceA>());
Assert.That(resolved.Service2, Is.InstanceOf<NoDependencyServiceA>());
}

[Test]
public void RegisterValueTypeFromFunc()
{
Expand Down Expand Up @@ -442,7 +442,7 @@ public void RegisterWithParameter()
var resolved = container.Resolve<HasMethodInjection>();
Assert.That(resolved.Service2, Is.EqualTo(paramValue));
}

{
var builder = new ContainerBuilder();
builder.Register<I2, NoDependencyServiceA>(Lifetime.Scoped);
Expand Down Expand Up @@ -601,8 +601,10 @@ public void OnContainerDisposeCallback()

builder.Register<NoDependencyServiceA>(Lifetime.Scoped);
builder.Register<NoDependencyServiceB>(Lifetime.Scoped);
builder.RegisterDisposeCallback(resolver => resolvedJustBeforeDispose = resolver.Resolve<NoDependencyServiceA>());
builder.RegisterDisposeCallback(resolver => resolvedJustBeforeDispose2 = resolver.Resolve<NoDependencyServiceB>());
builder.RegisterDisposeCallback(resolver =>
resolvedJustBeforeDispose = resolver.Resolve<NoDependencyServiceA>());
builder.RegisterDisposeCallback(resolver =>
resolvedJustBeforeDispose2 = resolver.Resolve<NoDependencyServiceB>());

var container = builder.Build();

Expand All @@ -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<NoDependencyServiceA>(Lifetime.Transient);

var container = builder.Build();

Assert.That(container.TryResolve<NoDependencyServiceA>(out var obj1), Is.True);
Assert.That(container.TryResolve<NoDependencyServiceA>(out var obj2), Is.True);
Assert.That(container.TryResolve<NoDependencyServiceB>(out var obj3), Is.False);

Assert.That(obj1, Is.TypeOf<NoDependencyServiceA>());
Assert.That(obj2, Is.TypeOf<NoDependencyServiceA>());
Assert.That(obj1, Is.Not.EqualTo(obj2));
Assert.That(obj3, Is.Null);
}

[Test]
public void TryResolveSingleton()
{
var builder = new ContainerBuilder();
builder.Register<NoDependencyServiceA>(Lifetime.Singleton);

var container = builder.Build();
Assert.That(container.TryResolve<NoDependencyServiceA>(out var obj1), Is.True);
Assert.That(container.TryResolve<NoDependencyServiceA>(out var obj2), Is.True);
Assert.That(container.TryResolve<NoDependencyServiceB>(out var obj3), Is.False);

Assert.That(obj1, Is.TypeOf<NoDependencyServiceA>());
Assert.That(obj2, Is.TypeOf<NoDependencyServiceA>());
Assert.That(obj1, Is.EqualTo(obj2));
Assert.That(obj3, Is.Null);
}

[Test]
public void TryResolveScoped()
{
var builder = new ContainerBuilder();
builder.Register<DisposableServiceA>(Lifetime.Scoped);

var container = builder.Build();
Assert.That(container.TryResolve<DisposableServiceA>(out var obj1), Is.True);
Assert.That(container.TryResolve<DisposableServiceA>(out var obj2), Is.True);
Assert.That(container.TryResolve<DisposableServiceB>(out var obj3), Is.False);

Assert.That(obj1, Is.TypeOf<DisposableServiceA>());
Assert.That(obj2, Is.TypeOf<DisposableServiceA>());
Assert.That(obj1, Is.EqualTo(obj2));
Assert.That(obj3, Is.Null);

container.Dispose();

Assert.That(obj1.Disposed, Is.True);
}
}
}
57 changes: 51 additions & 6 deletions VContainer/Assets/VContainer/Runtime/Container.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,25 @@ public interface IObjectResolver : IDisposable
/// </remarks>
object Resolve(Type type);

/// <summary>
/// Try resolve from type
/// </summary>
/// <remarks>
/// This version of resolve looks for all of scopes
/// </remarks>
/// <returns>Successfully resolved</returns>
bool TryResolve(Type type, out object resolved);

/// <summary>
/// Resolve from meta with registration
/// </summary>
/// <remarks>
/// This version of resolve will look for instances from only the registration information already founds.
/// </remarks>
object Resolve(Registration registration);

IScopedObjectResolver CreateScope(Action<IContainerBuilder> installation = null);

void Inject(object instance);
bool TryGetRegistration(Type type, out Registration registration);
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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)
{
Expand Down
24 changes: 24 additions & 0 deletions VContainer/Assets/VContainer/Runtime/IObjectResolverExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,30 @@ public static class IObjectResolverExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Resolve<T>(this IObjectResolver resolver) => (T)resolver.Resolve(typeof(T));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryResolve<T>(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<T>(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)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,7 @@ public void Dispatch()
{
PlayerLoopHelper.EnsureInitialized();

EntryPointExceptionHandler exceptionHandler = null;
try
{
exceptionHandler = container.Resolve<EntryPointExceptionHandler>();
}
catch (VContainerException ex) when (ex.InvalidType == typeof(EntryPointExceptionHandler))
{
}
EntryPointExceptionHandler exceptionHandler = container.ResolveOrDefault<EntryPointExceptionHandler>();

var initializables = container.Resolve<ContainerLocal<IReadOnlyList<IInitializable>>>().Value;
for (var i = 0; i < initializables.Count; i++)
Expand Down

0 comments on commit b4a5514

Please sign in to comment.