Skip to content

Commit

Permalink
Merge branch 'develop' into SupportForValueTypesFuncRegistration
Browse files Browse the repository at this point in the history
  • Loading branch information
AlonTalmi authored Nov 14, 2023
2 parents 77b62ff + 8a1d59d commit a4c069c
Show file tree
Hide file tree
Showing 13 changed files with 278 additions and 8 deletions.
37 changes: 35 additions & 2 deletions VContainer/Assets/VContainer/Runtime/Container.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using VContainer.Diagnostics;
using VContainer.Internal;
Expand Down Expand Up @@ -55,17 +56,20 @@ public sealed class ScopedContainer : IScopedObjectResolver
readonly ConcurrentDictionary<Registration, Lazy<object>> sharedInstances = new ConcurrentDictionary<Registration, Lazy<object>>();
readonly CompositeDisposable disposables = new CompositeDisposable();
readonly Func<Registration, Lazy<object>> createInstance;
readonly IEnumerable<Action<IObjectResolver>> disposeCallbacks;

internal ScopedContainer(
Registry registry,
IObjectResolver root,
IScopedObjectResolver parent = null,
object applicationOrigin = null)
object applicationOrigin = null,
IEnumerable<Action<IObjectResolver>> disposeCallbacks = null)
{
Root = root;
Parent = parent;
ApplicationOrigin = applicationOrigin;
this.registry = registry;
this.disposeCallbacks = disposeCallbacks;
createInstance = registration =>
{
return new Lazy<object>(() => registration.SpawnInstance(this));
Expand Down Expand Up @@ -110,6 +114,8 @@ public bool TryGetRegistration(Type type, out Registration registration)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
EmitDisposeCallbacks();

disposables.Dispose();
sharedInstances.Clear();
}
Expand Down Expand Up @@ -163,6 +169,17 @@ internal Registration FindRegistration(Type type)
}
throw new VContainerException(type, $"No such registration of type: {type}");
}

void EmitDisposeCallbacks()
{
if (disposeCallbacks == null)
return;

foreach (var callback in disposeCallbacks)
{
callback(this);
}
}
}

public sealed class Container : IObjectResolver
Expand All @@ -175,8 +192,9 @@ public sealed class Container : IObjectResolver
readonly ConcurrentDictionary<Registration, Lazy<object>> sharedInstances = new ConcurrentDictionary<Registration, Lazy<object>>();
readonly CompositeDisposable disposables = new CompositeDisposable();
readonly Func<Registration, Lazy<object>> createInstance;
readonly IEnumerable<Action<IObjectResolver>> disposeCallbacks;

internal Container(Registry registry, object applicationOrigin = null)
internal Container(Registry registry, object applicationOrigin = null, IEnumerable<Action<IObjectResolver>> disposeCallbacks = null)
{
this.registry = registry;
rootScope = new ScopedContainer(registry, this, applicationOrigin: applicationOrigin);
Expand All @@ -187,6 +205,8 @@ internal Container(Registry registry, object applicationOrigin = null)
};

ApplicationOrigin = applicationOrigin;

this.disposeCallbacks = disposeCallbacks;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -223,11 +243,24 @@ public void Inject(object instance)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
EmitDisposeCallbacks();

rootScope.Dispose();
disposables.Dispose();
sharedInstances.Clear();
}

void EmitDisposeCallbacks()
{
if (disposeCallbacks == null)
return;

foreach (var callback in disposeCallbacks)
{
callback(this);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
object ResolveCore(Registration registration)
{
Expand Down
14 changes: 12 additions & 2 deletions VContainer/Assets/VContainer/Runtime/ContainerBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public interface IContainerBuilder

T Register<T>(T registrationBuilder) where T : RegistrationBuilder;
void RegisterBuildCallback(Action<IObjectResolver> container);
void RegisterDisposeCallback(Action<IObjectResolver> callback);
bool Exists(Type type, bool includeInterfaceTypes = false);
}

Expand All @@ -36,7 +37,7 @@ internal ScopedContainerBuilder(IObjectResolver root, IScopedObjectResolver pare
public IScopedObjectResolver BuildScope()
{
var registry = BuildRegistry();
var container = new ScopedContainer(registry, root, parent, ApplicationOrigin);
var container = new ScopedContainer(registry, root, parent, ApplicationOrigin, disposeCallbacks?.ToArray());
container.Diagnostics = Diagnostics;
EmitCallbacks(container);
return container;
Expand Down Expand Up @@ -70,6 +71,7 @@ public DiagnosticsCollector Diagnostics

readonly List<RegistrationBuilder> registrationBuilders = new List<RegistrationBuilder>();
List<Action<IObjectResolver>> buildCallbacks;
protected List<Action<IObjectResolver>> disposeCallbacks;
DiagnosticsCollector diagnostics;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -88,6 +90,14 @@ public void RegisterBuildCallback(Action<IObjectResolver> callback)
buildCallbacks.Add(callback);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RegisterDisposeCallback(Action<IObjectResolver> callback)
{
if (disposeCallbacks == null)
disposeCallbacks = new List<Action<IObjectResolver>>();
disposeCallbacks.Add(callback);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists(Type type, bool includeInterfaceTypes = false)
{
Expand All @@ -106,7 +116,7 @@ public bool Exists(Type type, bool includeInterfaceTypes = false)
public virtual IObjectResolver Build()
{
var registry = BuildRegistry();
var container = new Container(registry, ApplicationOrigin);
var container = new Container(registry, ApplicationOrigin, disposeCallbacks?.ToArray());
container.Diagnostics = Diagnostics;
EmitCallbacks(container);
return container;
Expand Down
47 changes: 47 additions & 0 deletions VContainer/Assets/VContainer/Runtime/ContainerBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,40 +12,87 @@ public static RegistrationBuilder Register(
Type type,
Lifetime lifetime)
=> builder.Register(new RegistrationBuilder(type, lifetime));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RegistrationBuilder Register(
this IContainerBuilder builder,
Type type,
Lifetime lifetime,
Action<object, IObjectResolver> callback)
=> builder.Register(new RegistrationBuilderWithCallback(type, lifetime, callback));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RegistrationBuilder Register<T>(
this IContainerBuilder builder,
Lifetime lifetime)
=> builder.Register(typeof(T), lifetime);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RegistrationBuilder Register<T>(
this IContainerBuilder builder,
Lifetime lifetime,
Action<T, IObjectResolver> callback)
=> builder.Register(typeof(T), lifetime, (instance, resolver) => callback((T)instance, resolver));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RegistrationBuilder Register<TInterface, TImplement>(
this IContainerBuilder builder,
Lifetime lifetime)
where TImplement : TInterface
=> builder.Register<TImplement>(lifetime).As<TInterface>();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RegistrationBuilder Register<TInterface, TImplement>(
this IContainerBuilder builder,
Lifetime lifetime,
Action<TImplement, IObjectResolver> callback)
where TImplement : TInterface
=> builder.Register(lifetime, callback).As<TInterface>();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RegistrationBuilder Register<TInterface1, TInterface2, TImplement>(
this IContainerBuilder builder,
Lifetime lifetime)
where TImplement : TInterface1, TInterface2
=> builder.Register<TImplement>(lifetime).As(typeof(TInterface1), typeof(TInterface2));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RegistrationBuilder Register<TInterface1, TInterface2, TImplement>(
this IContainerBuilder builder,
Lifetime lifetime,
Action<TImplement, IObjectResolver> callback)
where TImplement : TInterface1, TInterface2
=> builder.Register(lifetime, callback).As(typeof(TInterface1), typeof(TInterface2));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RegistrationBuilder Register<TInterface1, TInterface2, TInterface3, TImplement>(
this IContainerBuilder builder,
Lifetime lifetime)
where TImplement : TInterface1, TInterface2, TInterface3
=> builder.Register<TImplement>(lifetime).As(typeof(TInterface1), typeof(TInterface2), typeof(TInterface3));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RegistrationBuilder Register<TInterface1, TInterface2, TInterface3, TImplement>(
this IContainerBuilder builder,
Lifetime lifetime,
Action<TImplement, IObjectResolver> callback)
where TImplement : TInterface1, TInterface2, TInterface3
=> builder.Register(lifetime, callback).As(typeof(TInterface1), typeof(TInterface2), typeof(TInterface3));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RegistrationBuilder Register<TInterface>(
this IContainerBuilder builder,
Func<IObjectResolver, TInterface> implementationConfiguration,
Lifetime lifetime)
=> builder.Register(new FuncRegistrationBuilder(resolver => implementationConfiguration(resolver), typeof(TInterface), lifetime));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RegistrationBuilder Register<TInterface>(
this IContainerBuilder builder,
Func<IObjectResolver, TInterface> implementationConfiguration,
Lifetime lifetime,
Action<TInterface, IObjectResolver> callback)
=> builder.Register(new FuncRegistrationBuilderWithCallback(resolver => implementationConfiguration(resolver), typeof(TInterface), lifetime, (instance, resolver) => callback((TInterface)instance, resolver)));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RegistrationBuilder RegisterInstance<TInterface>(
Expand Down
2 changes: 1 addition & 1 deletion VContainer/Assets/VContainer/Runtime/IInjectParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ namespace VContainer
public interface IInjectParameter
{
bool Match(Type parameterType, string parameterName);
object Value { get; }
object GetValue(IObjectResolver resolver);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static object ResolveOrParameter(
var parameter = parameters[i];
if (parameter.Match(parameterType, parameterName))
{
return parameter.Value;
return parameter.GetValue(resolver);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;

namespace VContainer.Internal
{
sealed class FuncRegistrationBuilderWithCallback : RegistrationBuilder
{
readonly Func<IObjectResolver, object> implementationProvider;
readonly Action<object, IObjectResolver> callback;

public FuncRegistrationBuilderWithCallback(
Func<IObjectResolver, object> implementationProvider,
Type implementationType,
Lifetime lifetime,
Action<object, IObjectResolver> callback) : base(implementationType, lifetime)
{
this.implementationProvider = implementationProvider;
this.callback = callback;
}

public override Registration Build()
{
var spawner = new InstanceProviderCallbackDecorator(new FuncInstanceProvider(implementationProvider), callback);
return new Registration(ImplementationType, Lifetime, InterfaceTypes, spawner);
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 50 additions & 2 deletions VContainer/Assets/VContainer/Runtime/Internal/InjectParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace VContainer.Internal
sealed class TypedParameter : IInjectParameter
{
public readonly Type Type;
public object Value { get; }
public readonly object Value;

public TypedParameter(Type type, object value)
{
Expand All @@ -14,12 +14,36 @@ public TypedParameter(Type type, object value)
}

public bool Match(Type parameterType, string _) => parameterType == Type;

public object GetValue(IObjectResolver _)
{
return Value;
}
}

sealed class FuncTypedParameter : IInjectParameter
{
public readonly Type Type;
public readonly Func<IObjectResolver, object> Func;

public FuncTypedParameter(Type type, Func<IObjectResolver, object> func)
{
Type = type;
Func = func;
}

public bool Match(Type parameterType, string _) => parameterType == Type;

public object GetValue(IObjectResolver resolver)
{
return Func(resolver);
}
}

sealed class NamedParameter : IInjectParameter
{
public readonly string Name;
public object Value { get; }
public readonly object Value;

public NamedParameter(string name, object value)
{
Expand All @@ -28,5 +52,29 @@ public NamedParameter(string name, object value)
}

public bool Match(Type _, string parameterName) => parameterName == Name;

public object GetValue(IObjectResolver _)
{
return Value;
}
}

sealed class FuncNamedParameter : IInjectParameter
{
public readonly string Name;
public readonly Func<IObjectResolver, object> Func;

public FuncNamedParameter(string name, Func<IObjectResolver, object> func)
{
Name = name;
Func = func;
}

public bool Match(Type _, string parameterName) => parameterName == Name;

public object GetValue(IObjectResolver resolver)
{
return Func(resolver);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;

namespace VContainer.Internal
{
sealed class InstanceProviderCallbackDecorator : IInstanceProvider
{
readonly Action<object, IObjectResolver> callback;
readonly IInstanceProvider instanceProvider;

public InstanceProviderCallbackDecorator(IInstanceProvider instanceProvider, Action<object, IObjectResolver> callback)
{
this.instanceProvider = instanceProvider;
this.callback = callback;
}

public object SpawnInstance(IObjectResolver resolver)
{
var instance = instanceProvider.SpawnInstance(resolver);
callback(instance, resolver);
return instance;
}
}
}
Loading

0 comments on commit a4c069c

Please sign in to comment.