#51: completely rework resolve

- introduce new method TryResolve that is used by internalResolve and tryGetConstructorResolveStack
- add own creator helper class
- fix nullable warnings
pull/57/head
Simon G 4 years ago
parent fe8642fd22
commit 3b11e24e7b
  1. 24
      LightweightIocContainer/Creator.cs
  2. 8
      LightweightIocContainer/Factories/TypedFactory.cs
  3. 4
      LightweightIocContainer/GenericMethodCaller.cs
  4. 2
      LightweightIocContainer/Installers/AssemblyInstaller.cs
  5. 441
      LightweightIocContainer/IocContainer.cs
  6. 14
      LightweightIocContainer/LightweightIocContainer.xml
  7. 18
      LightweightIocContainer/ResolvePlaceholders/IInternalToBeResolvedPlaceholder.cs
  8. 31
      LightweightIocContainer/ResolvePlaceholders/InternalFactoryMethodPlaceholder.cs
  9. 15
      LightweightIocContainer/ResolvePlaceholders/InternalToBeResolvedPlaceholder.cs

@ -0,0 +1,24 @@
// Author: Gockner, Simon
// Created: 2021-12-14
// Copyright(c) 2021 SimonG. All Rights Reserved.
using System;
namespace LightweightIocContainer;
/// <summary>
/// Helper for dynamic instance creation
/// </summary>
internal static class Creator
{
/// <summary>
/// Creates an instance of the given <see cref="Type"/> with the given arguments
/// </summary>
/// <param name="type">The given <see cref="Type"/></param>
/// <param name="arguments">The given arguments</param>
/// <typeparam name="T">The <see cref="Type"/> that is returned</typeparam>
/// <returns>A new instance of the given <see cref="Type"/></returns>
/// <exception cref="InvalidOperationException">The given type could not be created</exception>
public static T CreateInstance<T>(Type type, params object?[]? arguments) =>
(T) (Activator.CreateInstance(type, arguments) ?? throw new InvalidOperationException($"Type {type} could not be created."));
}

@ -92,11 +92,11 @@ namespace LightweightIocContainer.Factories
} }
else else
{ {
MethodInfo? emptyArray = typeof(Array).GetMethod(nameof(Array.Empty))?.MakeGenericMethod(typeof(object)); MethodInfo emptyArray = typeof(Array).GetMethod(nameof(Array.Empty))!.MakeGenericMethod(typeof(object));
generator.EmitCall(OpCodes.Call, emptyArray, null); generator.EmitCall(OpCodes.Call, emptyArray, null);
} }
generator.EmitCall(OpCodes.Callvirt, typeof(IResolver).GetMethod(nameof(IResolver.Resolve), new[] { typeof(object[]) })?.MakeGenericMethod(createMethod.ReturnType), null); generator.EmitCall(OpCodes.Callvirt, typeof(IResolver).GetMethod(nameof(IResolver.Resolve), new[] { typeof(object[]) })!.MakeGenericMethod(createMethod.ReturnType), null);
generator.Emit(OpCodes.Castclass, createMethod.ReturnType); generator.Emit(OpCodes.Castclass, createMethod.ReturnType);
generator.Emit(OpCodes.Ret); generator.Emit(OpCodes.Ret);
} }
@ -127,7 +127,7 @@ namespace LightweightIocContainer.Factories
multitonClearGenerator.Emit(OpCodes.Ldarg_0); multitonClearGenerator.Emit(OpCodes.Ldarg_0);
multitonClearGenerator.Emit(OpCodes.Ldfld, containerFieldBuilder); multitonClearGenerator.Emit(OpCodes.Ldfld, containerFieldBuilder);
multitonClearGenerator.EmitCall(OpCodes.Callvirt, typeof(IIocContainer).GetMethod(nameof(IIocContainer.ClearMultitonInstances))?.MakeGenericMethod(typeToClear), null); multitonClearGenerator.EmitCall(OpCodes.Callvirt, typeof(IIocContainer).GetMethod(nameof(IIocContainer.ClearMultitonInstances))!.MakeGenericMethod(typeToClear), null);
multitonClearGenerator.Emit(OpCodes.Ret); multitonClearGenerator.Emit(OpCodes.Ret);
} }
else else
@ -136,7 +136,7 @@ namespace LightweightIocContainer.Factories
} }
} }
return (TFactory) Activator.CreateInstance(typeBuilder.CreateTypeInfo()?.AsType(), container); return Creator.CreateInstance<TFactory>(typeBuilder.CreateTypeInfo()!.AsType(), container);
} }
} }
} }

@ -24,7 +24,7 @@ namespace LightweightIocContainer
/// <returns>The result of invoking the method</returns> /// <returns>The result of invoking the method</returns>
/// <exception cref="GenericMethodNotFoundException">Could not find the generic method</exception> /// <exception cref="GenericMethodNotFoundException">Could not find the generic method</exception>
/// <exception cref="Exception">Any <see cref="Exception"/> thrown after invoking the generic method</exception> /// <exception cref="Exception">Any <see cref="Exception"/> thrown after invoking the generic method</exception>
public static object Call(object caller, string functionName, Type genericParameter, BindingFlags bindingFlags, params object?[] parameters) public static object? Call(object caller, string functionName, Type genericParameter, BindingFlags bindingFlags, params object?[] parameters)
{ {
MethodInfo? method = caller.GetType().GetMethod(functionName, bindingFlags); MethodInfo? method = caller.GetType().GetMethod(functionName, bindingFlags);
MethodInfo? genericMethod = method?.MakeGenericMethod(genericParameter); MethodInfo? genericMethod = method?.MakeGenericMethod(genericParameter);
@ -52,7 +52,7 @@ namespace LightweightIocContainer
/// <returns>The result of invoking the method</returns> /// <returns>The result of invoking the method</returns>
/// <exception cref="GenericMethodNotFoundException">Could not find the generic method</exception> /// <exception cref="GenericMethodNotFoundException">Could not find the generic method</exception>
/// <exception cref="Exception">Any <see cref="Exception"/> thrown after invoking the generic method</exception> /// <exception cref="Exception">Any <see cref="Exception"/> thrown after invoking the generic method</exception>
public static object CallPrivate(object caller, string functionName, Type genericParameter, params object?[] parameters) => public static object? CallPrivate(object caller, string functionName, Type genericParameter, params object?[] parameters) =>
Call(caller, functionName, genericParameter, BindingFlags.NonPublic | BindingFlags.Instance, parameters); Call(caller, functionName, genericParameter, BindingFlags.NonPublic | BindingFlags.Instance, parameters);
} }
} }

@ -29,7 +29,7 @@ namespace LightweightIocContainer.Installers
if (!typeof(IIocInstaller).IsAssignableFrom(type) || type.IsNestedPrivate) if (!typeof(IIocInstaller).IsAssignableFrom(type) || type.IsNestedPrivate)
continue; continue;
Installers.Add((IIocInstaller) Activator.CreateInstance(type)); Installers.Add(Creator.CreateInstance<IIocInstaller>(type));
} }
} }

@ -270,182 +270,271 @@ namespace LightweightIocContainer
/// <param name="resolveStack">The current resolve stack</param> /// <param name="resolveStack">The current resolve stack</param>
/// <returns>An instance of the given <see cref="Type"/></returns> /// <returns>An instance of the given <see cref="Type"/></returns>
/// <exception cref="InternalResolveException">Could not find function <see cref="ResolveInternal{T}"/></exception> /// <exception cref="InternalResolveException">Could not find function <see cref="ResolveInternal{T}"/></exception>
internal object Resolve(Type type, object?[]? arguments, List<Type>? resolveStack) => internal object? Resolve(Type type, object?[]? arguments, List<Type>? resolveStack) =>
GenericMethodCaller.CallPrivate(this, nameof(ResolveInternal), type, arguments, resolveStack); GenericMethodCaller.CallPrivate(this, nameof(ResolveInternal), type, arguments, resolveStack);
/// <summary>
/// Gets an instance of a given registered <see cref="Type"/>
/// </summary>
/// <typeparam name="T">The registered <see cref="Type"/></typeparam>
/// <param name="arguments">The constructor arguments</param>
/// <param name="resolveStack">The current resolve stack</param>
/// <returns>An instance of the given registered <see cref="Type"/></returns>
private T ResolveInternal<T>(object[]? arguments, List<Type>? resolveStack = null) => ResolveInstance<T>(TryResolve<T>(arguments, resolveStack));
/// <summary>
/// Tries to resolve the given <see cref="Type"/> with the given arguments
/// </summary>
/// <param name="arguments">The given arguments</param>
/// <param name="resolveStack">The current resolve stack</param>
/// <typeparam name="T">The registered <see cref="Type"/></typeparam>
/// <returns>An instance of the given registered <see cref="Type"/>, an <see cref="InternalToBeResolvedPlaceholder"/> if parameters need to be resolved or an <see cref="InternalFactoryMethodPlaceholder{T}"/> if a factory method is used to create an instance</returns>
/// <exception cref="TypeNotRegisteredException">The given <see cref="Type"/> is not registered</exception>
/// <exception cref="InvalidRegistrationException">An interface was registered without an implementation or factory method</exception>
/// <exception cref="MultitonResolveException">Tried resolving a multiton without scope argument</exception>
/// <exception cref="NoMatchingConstructorFoundException">No matching constructor for the given <see cref="Type"/> found</exception>
/// <exception cref="InternalResolveException">Getting resolve stack failed without exception</exception>
private object TryResolve<T>(object[]? arguments, List<Type>? resolveStack)
{
IRegistration registration = FindRegistration<T>() ?? throw new TypeNotRegisteredException(typeof(T));
List<Type> internalResolveStack = resolveStack == null ? new List<Type>() : new List<Type>(resolveStack);
internalResolveStack = CheckForCircularDependencies<T>(internalResolveStack);
object? existingInstance = TryGetExistingInstance<T>(registration, arguments);
if (existingInstance != null)
return existingInstance;
if (registration is ISingleTypeRegistration<T> singleTypeRegistration)
{
if (singleTypeRegistration.InterfaceType.IsInterface && singleTypeRegistration.FactoryMethod == null)
throw new InvalidRegistrationException($"Can't register an interface without its implementation type or without a factory method (Type: {singleTypeRegistration.InterfaceType}).");
if (singleTypeRegistration.FactoryMethod != null)
return new InternalFactoryMethodPlaceholder<T>(singleTypeRegistration);
}
if (registration is IWithParametersInternal { Parameters: { } } registrationWithParameters)
arguments = UpdateArgumentsWithRegistrationParameters(registrationWithParameters, arguments);
Type registeredType = GetType<T>(registration);
(bool result, List<object?>? parametersToResolve, NoMatchingConstructorFoundException? exception) =
TryGetTypeResolveStack(registeredType, arguments, internalResolveStack);
if (registration is IMultitonRegistration multitonRegistration)
{
if (arguments == null || !arguments.Any())
throw new MultitonResolveException("Can not resolve multiton without arguments.", registration.InterfaceType);
object multitonScopeArgument = TryGetMultitonScopeArgument(multitonRegistration, arguments);
parametersToResolve ??= new List<object?>();
parametersToResolve.Add(multitonScopeArgument);
}
if (result)
return new InternalToBeResolvedPlaceholder(registeredType, registration, parametersToResolve);
if (exception != null)
throw exception;
throw new InternalResolveException("Getting resolve stack failed without exception.");
}
/// <summary>
/// Tries to resolve the given <see cref="Type"/> with the given arguments without generic arguments
/// </summary>
/// <param name="type">The registered <see cref="Type"/></param>
/// <param name="arguments">The given arguments</param>
/// <param name="resolveStack">The current resolve stack</param>
/// <returns>An instance of the given registered <see cref="Type"/>, an <see cref="InternalToBeResolvedPlaceholder"/> if parameters need to be resolved or an <see cref="InternalFactoryMethodPlaceholder{T}"/> if a factory method is used to create an instance</returns>
/// <exception cref="TypeNotRegisteredException">The given <see cref="Type"/> is not registered</exception>
/// <exception cref="InvalidRegistrationException">An interface was registered without an implementation or factory method</exception>
/// <exception cref="MultitonResolveException">Tried resolving a multiton without scope argument</exception>
/// <exception cref="NoMatchingConstructorFoundException">No matching constructor for the given <see cref="Type"/> found</exception>
/// <exception cref="InternalResolveException">Getting resolve stack failed without exception</exception>
private object? TryResolveNonGeneric(Type type, object[]? arguments, List<Type> resolveStack) =>
GenericMethodCaller.CallPrivate(this, nameof(TryResolve), type, arguments, resolveStack);
/// <summary> /// <summary>
/// Recursively resolve a <see cref="Type"/> with the given parameters for an <see cref="InternalToBeResolvedPlaceholder"/> /// Recursively resolve a <see cref="Type"/> with the given parameters for an <see cref="InternalToBeResolvedPlaceholder"/>
/// </summary> /// </summary>
/// <param name="toBeResolvedPlaceholder">The <see cref="InternalToBeResolvedPlaceholder"/> that includes the type and resolve stack</param> /// <param name="toBeResolvedPlaceholder">The <see cref="InternalToBeResolvedPlaceholder"/> that includes the type and resolve stack</param>
/// <param name="resolveStack">The current resolve stack</param>
/// <returns>A recursively resolved instance of the given <see cref="Type"/></returns> /// <returns>A recursively resolved instance of the given <see cref="Type"/></returns>
private object Resolve(InternalToBeResolvedPlaceholder toBeResolvedPlaceholder, List<Type> resolveStack) private T ResolvePlaceholder<T>(InternalToBeResolvedPlaceholder toBeResolvedPlaceholder)
{ {
if (toBeResolvedPlaceholder.Parameters == null) if (toBeResolvedPlaceholder.Parameters == null)
return Resolve(toBeResolvedPlaceholder.ResolvedType, null, resolveStack); return CreateInstance<T>(toBeResolvedPlaceholder.ResolvedRegistration, null);
List<object> parameters = new(); List<object?> parameters = new();
foreach (object parameter in toBeResolvedPlaceholder.Parameters) foreach (object? parameter in toBeResolvedPlaceholder.Parameters)
{ {
if (parameter is InternalToBeResolvedPlaceholder internalToBeResolvedPlaceholder) if (parameter != null)
parameters.Add(Resolve(internalToBeResolvedPlaceholder, resolveStack)); {
Type type = parameter is IInternalToBeResolvedPlaceholder internalToBeResolvedPlaceholder ?
internalToBeResolvedPlaceholder.ResolvedType : parameter.GetType();
parameters.Add(ResolveInstanceNonGeneric(type, parameter));
}
else else
parameters.Add(parameter); parameters.Add(parameter);
} }
return Resolve(toBeResolvedPlaceholder.ResolvedType, parameters.ToArray(), resolveStack); return CreateInstance<T>(toBeResolvedPlaceholder.ResolvedRegistration, parameters.ToArray());
} }
/// <summary> /// <summary>
/// Gets an instance of a given registered <see cref="Type"/> /// Resolve the given object instance
/// </summary> /// </summary>
/// <typeparam name="T">The registered <see cref="Type"/></typeparam> /// <param name="resolvedObject">The given resolved object</param>
/// <param name="arguments">The constructor arguments</param> /// <typeparam name="T">The <see cref="Type"/> of the returned instance</typeparam>
/// <param name="resolveStack">The current resolve stack</param> /// <returns>An instance of the given resolved object</returns>
/// <returns>An instance of the given registered <see cref="Type"/></returns> /// <exception cref="InternalResolveException">Resolve returned wrong type</exception>
/// <exception cref="TypeNotRegisteredException">The given <see cref="Type"/> is not registered in this <see cref="IocContainer"/></exception> private T ResolveInstance<T>(object resolvedObject) =>
/// <exception cref="UnknownRegistrationException">The registration for the given <see cref="Type"/> has an unknown <see cref="Type"/></exception> resolvedObject switch
private T ResolveInternal<T>(object[]? arguments, List<Type>? resolveStack = null)
{
IRegistration registration = FindRegistration<T>() ?? throw new TypeNotRegisteredException(typeof(T));
//Circular dependency check
resolveStack = CheckForCircularDependencies<T>(resolveStack);
T resolvedInstance = registration switch
{ {
RegistrationBase { Lifestyle: Lifestyle.Singleton } defaultRegistration => GetOrCreateSingletonInstance<T>(defaultRegistration, arguments, resolveStack), T instance => instance,
RegistrationBase { Lifestyle: Lifestyle.Multiton } and IMultitonRegistration multitonRegistration => GetOrCreateMultitonInstance<T>(multitonRegistration, arguments, resolveStack), InternalToBeResolvedPlaceholder toBeResolvedPlaceholder => ResolvePlaceholder<T>(toBeResolvedPlaceholder),
RegistrationBase defaultRegistration => CreateInstance<T>(defaultRegistration, arguments, resolveStack), InternalFactoryMethodPlaceholder<T> factoryMethodPlaceholder => factoryMethodPlaceholder.FactoryMethod(this),
ITypedFactoryRegistration<T> typedFactoryRegistration => typedFactoryRegistration.Factory.Factory, _ => throw new InternalResolveException("Resolve returned wrong type.")
_ => throw new UnknownRegistrationException($"There is no registration of type {registration.GetType().Name}.")
}; };
resolveStack.Remove(typeof(T)); //T was successfully resolved -> no circular dependency -> remove from resolve stack /// <summary>
/// Resolve the given object instance without generic arguments
return resolvedInstance; /// </summary>
} /// <param name="type">The <see cref="Type"/> of the returned instance</param>
/// <param name="resolveObject"></param>
/// <returns>An instance of the given resolved object</returns>
/// <exception cref="InternalResolveException">Resolve returned wrong type</exception>
private object? ResolveInstanceNonGeneric(Type type, object resolveObject) =>
GenericMethodCaller.CallPrivate(this, nameof(ResolveInstance), type, resolveObject);
/// <summary> /// <summary>
/// Gets or creates a singleton instance of a given <see cref="Type"/> /// Creates an instance of a given <see cref="Type"/>
/// </summary> /// </summary>
/// <typeparam name="T">The given <see cref="Type"/></typeparam> /// <typeparam name="T">The given <see cref="Type"/></typeparam>
/// <param name="registration">The registration of the given <see cref="Type"/></param> /// <param name="registration">The registration of the given <see cref="Type"/></param>
/// <param name="arguments">The arguments to resolve</param> /// <param name="arguments">The constructor arguments</param>
/// <param name="resolveStack">The current resolve stack</param> /// <returns>A newly created instance of the given <see cref="Type"/></returns>
/// <returns>An existing or newly created singleton instance of the given <see cref="Type"/></returns> private T CreateInstance<T>(IRegistration registration, object?[]? arguments)
private T GetOrCreateSingletonInstance<T>(IRegistration registration, object[]? arguments, List<Type> resolveStack)
{ {
Type type = GetType<T>(registration); T instance;
if (registration is IOpenGenericRegistration openGenericRegistration)
object? instance = TryGetSingletonInstance(type); {
if (instance != null) //create generic implementation type from generic arguments of T
return (T) instance; Type genericImplementationType = openGenericRegistration.ImplementationType.MakeGenericType(typeof(T).GenericTypeArguments);
instance = Creator.CreateInstance<T>(genericImplementationType, arguments);
}
else if (registration is ISingleTypeRegistration<T> singleTypeRegistration)
instance = singleTypeRegistration.FactoryMethod == null ? Creator.CreateInstance<T>(singleTypeRegistration.InterfaceType, arguments) : singleTypeRegistration.FactoryMethod(this);
else if (registration is ILifestyleProvider { Lifestyle: Lifestyle.Multiton } and IMultitonRegistration multitonRegistration)
instance = CreateMultitonInstance<T>(multitonRegistration, arguments);
else if (registration is ITypedRegistration defaultRegistration)
instance = Creator.CreateInstance<T>(defaultRegistration.ImplementationType, arguments);
else
throw new UnknownRegistrationException($"There is no registration of type {registration.GetType().Name}.");
//if it doesn't already exist create a new instance and add it to the list if (registration is ILifestyleProvider { Lifestyle: Lifestyle.Singleton })
T newInstance = CreateInstance<T>(registration, arguments, resolveStack); _singletons.Add((GetType<T>(registration), instance));
_singletons.Add((type, newInstance));
return newInstance; if (registration is IOnCreate onCreateRegistration)
onCreateRegistration.OnCreateAction?.Invoke(instance); //TODO: Allow async OnCreateAction?
return instance;
} }
/// <summary> /// <summary>
/// Try to get an existing singleton instance for a given <see cref="Type"/> /// Try to get an already existing instance (factory, singleton, multiton)
/// </summary> /// </summary>
/// <param name="type">The given <see cref="Type"/></param> /// <param name="registration">The given <see cref="IRegistration"/></param>
/// <returns>A singleton instance if existing for the given <see cref="Type"/>, null if not</returns> /// <param name="arguments">The given arguments</param>
private object? TryGetSingletonInstance(Type type) => _singletons.FirstOrDefault(s => s.type == type).instance; //if a singleton instance exists return it /// <typeparam name="T">The <see cref="Type"/> of the instance</typeparam>
/// <returns>An already existing instance if possible, null if not</returns>
private object? TryGetExistingInstance<T>(IRegistration registration, IReadOnlyList<object>? arguments) =>
registration switch
{
ITypedFactoryRegistration<T> typedFactoryRegistration => typedFactoryRegistration.Factory.Factory,
ILifestyleProvider { Lifestyle: Lifestyle.Singleton } => TryGetSingletonInstance<T>(registration),
ILifestyleProvider { Lifestyle: Lifestyle.Multiton } and IMultitonRegistration multitonRegistration => TryGetMultitonInstance(multitonRegistration, arguments),
_ => null
};
/// <summary>
/// Try to get an existing singleton instance for a given <see cref="IRegistration"/>
/// </summary>
/// <param name="registration">The <see cref="IRegistration"/></param>
/// <returns>A singleton instance if existing for the given <see cref="IRegistration"/>, null if not</returns>
private object? TryGetSingletonInstance<T>(IRegistration registration) =>
_singletons.FirstOrDefault(s => s.type == GetType<T>(registration)).instance; //if a singleton instance exists return it
/// <summary> /// <summary>
/// Gets or creates a multiton instance of a given <see cref="Type"/> /// Try to get an existing multiton instance for a given <see cref="IMultitonRegistration"/>
/// </summary> /// </summary>
/// <typeparam name="T">The given <see cref="Type"/></typeparam> /// <param name="registration">The given <see cref="IMultitonRegistration"/></param>
/// <param name="registration">The registration of the given <see cref="Type"/></param> /// <param name="arguments">The given arguments</param>
/// <param name="arguments">The arguments to resolve</param> /// <returns>A multiton instance if existing for the given <see cref="IRegistration"/>, null if not</returns>
/// <param name="resolveStack">The current resolve stack</param> /// <exception cref="MultitonResolveException">Tried resolving a multiton without scope argument</exception>
/// <returns>An existing or newly created multiton instance of the given <see cref="Type"/></returns> private object? TryGetMultitonInstance(IMultitonRegistration registration, IReadOnlyList<object>? arguments)
/// <exception cref="MultitonResolveException">No arguments given</exception>
/// <exception cref="MultitonResolveException">Scope argument not given</exception>
private T GetOrCreateMultitonInstance<T>(IMultitonRegistration registration, object[]? arguments, List<Type> resolveStack)
{ {
if (arguments == null || !arguments.Any()) if (arguments == null || !arguments.Any())
throw new MultitonResolveException("Can not resolve multiton without arguments.", typeof(T)); throw new MultitonResolveException("Can not resolve multiton without arguments.", registration.InterfaceType);
object scopeArgument = arguments[0]; object scopeArgument = TryGetMultitonScopeArgument(registration, arguments);
if (scopeArgument.GetType() != registration.Scope && !registration.Scope.IsInstanceOfType(scopeArgument))
throw new MultitonResolveException($"Can not resolve multiton without the first argument being the scope (should be of type {registration.Scope}).", typeof(T));
//if a multiton for the given scope exists return it //if a multiton for the given scope exists return it
var instances = _multitons.FirstOrDefault(m => m.type == registration.ImplementationType && m.scope == registration.Scope).instances; //get instances for the given type and scope (use implementation type to resolve the correct instance for multiple multiton registrations as well) var matchingMultitons = _multitons.FirstOrDefault(m => m.type == registration.ImplementationType && m.scope == registration.Scope); //get instances for the given type and scope (use implementation type to resolve the correct instance for multiple multiton registrations as well)
if (instances != null) if (matchingMultitons == default)
{ return null;
if (instances.TryGetValue(scopeArgument, out object? instance) && instance != null)
return (T) instance;
T createdInstance = CreateInstance<T>(registration, arguments, resolveStack);
instances.Add(scopeArgument, createdInstance);
return createdInstance;
}
T newInstance = CreateInstance<T>(registration, arguments, resolveStack); return matchingMultitons.instances.TryGetValue(scopeArgument, out object? instance) && instance != null ? instance : null;
}
ConditionalWeakTable<object, object?> weakTable = new(); /// <summary>
weakTable.Add(scopeArgument, newInstance); /// Try to get the multiton scope argument for a given <see cref="IMultitonRegistration"/>
/// </summary>
/// <param name="registration">The given <see cref="IMultitonRegistration"/></param>
/// <param name="arguments">The given arguments</param>
/// <returns>The multiton scope argument for the given <see cref="IMultitonRegistration"/></returns>
/// <exception cref="MultitonResolveException">Tried resolving a multiton without correct scope argument</exception>
private object TryGetMultitonScopeArgument(IMultitonRegistration registration, IReadOnlyList<object?> arguments)
{
object? scopeArgument = arguments[0];
if (scopeArgument?.GetType() != registration.Scope && !registration.Scope.IsInstanceOfType(scopeArgument))
throw new MultitonResolveException($"Can not resolve multiton without the first argument being the scope (should be of type {registration.Scope}).", registration.InterfaceType);
_multitons.Add((registration.ImplementationType, registration.Scope, weakTable)); return scopeArgument;
return newInstance;
} }
/// <summary> /// <summary>
/// Creates an instance of a given <see cref="Type"/> /// Gets or creates a multiton instance of a given <see cref="Type"/>
/// </summary> /// </summary>
/// <typeparam name="T">The given <see cref="Type"/></typeparam> /// <typeparam name="T">The given <see cref="Type"/></typeparam>
/// <param name="registration">The registration of the given <see cref="Type"/></param> /// <param name="registration">The registration of the given <see cref="Type"/></param>
/// <param name="arguments">The constructor arguments</param> /// <param name="arguments">The arguments to resolve</param>
/// <param name="resolveStack">The current resolve stack</param> /// <returns>An existing or newly created multiton instance of the given <see cref="Type"/></returns>
/// <returns>A newly created instance of the given <see cref="Type"/></returns> /// <exception cref="MultitonResolveException">No arguments given</exception>
private T CreateInstance<T>(IRegistration registration, object[]? arguments, List<Type> resolveStack) /// <exception cref="MultitonResolveException">Scope argument not given</exception>
private T CreateMultitonInstance<T>(IMultitonRegistration registration, object?[]? arguments)
{ {
if (registration is IWithParametersInternal { Parameters: { } } registrationWithParameters) if (arguments == null || !arguments.Any())
arguments = UpdateArgumentsWithRegistrationParameters(registrationWithParameters, arguments); throw new MultitonResolveException("Can not resolve multiton without arguments.", registration.InterfaceType);
object scopeArgument = TryGetMultitonScopeArgument(registration, arguments);
T instance; //if a multiton for the given scope exists return it
if (registration is IOpenGenericRegistration openGenericRegistration) var matchingMultitons = _multitons.FirstOrDefault(m => m.type == registration.ImplementationType && m.scope == registration.Scope); //get instances for the given type and scope (use implementation type to resolve the correct instance for multiple multiton registrations as well)
if (matchingMultitons != default)
{ {
arguments = ResolveTypeCreationArguments(openGenericRegistration.ImplementationType, arguments, resolveStack); T createdInstance = Creator.CreateInstance<T>(registration.ImplementationType, arguments[1..]);
matchingMultitons.instances.Add(scopeArgument, createdInstance);
//create generic implementation type from generic arguments of T
Type genericImplementationType = openGenericRegistration.ImplementationType.MakeGenericType(typeof(T).GenericTypeArguments);
instance = (T) Activator.CreateInstance(genericImplementationType, arguments); return createdInstance;
}
else if (registration is ITypedRegistration defaultRegistration)
{
arguments = ResolveTypeCreationArguments(defaultRegistration.ImplementationType, arguments, resolveStack);
instance = (T) Activator.CreateInstance(defaultRegistration.ImplementationType, arguments);
} }
else if (registration is ISingleTypeRegistration<T> singleTypeRegistration)
{
if (singleTypeRegistration.InterfaceType.IsInterface && singleTypeRegistration.FactoryMethod == null)
throw new InvalidRegistrationException($"Can't register an interface without its implementation type or without a factory method (Type: {singleTypeRegistration.InterfaceType}).");
if (singleTypeRegistration.FactoryMethod == null) //type registration without interface -> just create this type T newInstance = Creator.CreateInstance<T>(registration.ImplementationType, arguments[1..]);
{ _multitons.Add((registration.ImplementationType, registration.Scope, new ConditionalWeakTable<object, object?> { { scopeArgument, newInstance } }));
arguments = ResolveTypeCreationArguments(singleTypeRegistration.InterfaceType, arguments, resolveStack);
instance = (T) Activator.CreateInstance(singleTypeRegistration.InterfaceType, arguments);
}
else //factory method set to create the instance
instance = singleTypeRegistration.FactoryMethod(this);
}
else
throw new UnknownRegistrationException($"There is no registration of type {registration.GetType().Name}.");
if (registration is IOnCreate onCreateRegistration)
onCreateRegistration.OnCreateAction?.Invoke(instance); //TODO: Allow async OnCreateAction?
return instance; return newInstance;
} }
/// <summary> /// <summary>
@ -494,41 +583,6 @@ namespace LightweightIocContainer
return arguments; return arguments;
} }
/// <summary>
/// Resolve the missing type creation arguments
/// </summary>
/// <param name="type">The <see cref="Type"/> that will be created</param>
/// <param name="arguments">The existing arguments</param>
/// <param name="resolveStack">The current resolve stack</param>
/// <returns>An array of all needed constructor arguments to create the <see cref="Type"/></returns>
/// <exception cref="NoMatchingConstructorFoundException">No matching constructor was found for the given or resolvable arguments</exception>
private object[]? ResolveTypeCreationArguments(Type type, object[]? arguments, List<Type> resolveStack)
{
(bool result, List<object>? parameters, NoMatchingConstructorFoundException? exception) = TryGetTypeResolveStack(type, arguments, resolveStack);
if (result)
{
if (parameters == null)
return null;
List<object> constructorParameters = new();
foreach (object parameter in parameters)
{
if (parameter is InternalToBeResolvedPlaceholder toBeResolvedPlaceholder)
constructorParameters.Add(Resolve(toBeResolvedPlaceholder, resolveStack));
else
constructorParameters.Add(parameter);
}
return constructorParameters.ToArray();
}
if (exception != null)
throw exception;
return null;
}
/// <summary> /// <summary>
/// Try to get the resolve stack for a given <see cref="Type"/> /// Try to get the resolve stack for a given <see cref="Type"/>
/// </summary> /// </summary>
@ -540,7 +594,7 @@ namespace LightweightIocContainer
/// <para>parameters: The parameters needed to resolve the given <see cref="Type"/></para> /// <para>parameters: The parameters needed to resolve the given <see cref="Type"/></para>
/// <para>exception: A <see cref="NoMatchingConstructorFoundException"/> if no matching constructor was found</para> /// <para>exception: A <see cref="NoMatchingConstructorFoundException"/> if no matching constructor was found</para>
/// </returns> /// </returns>
private (bool result, List<object>? parameters, NoMatchingConstructorFoundException? exception) TryGetTypeResolveStack(Type type, object[]? arguments, List<Type> resolveStack) private (bool result, List<object?>? parameters, NoMatchingConstructorFoundException? exception) TryGetTypeResolveStack(Type type, object[]? arguments, List<Type> resolveStack)
{ {
NoMatchingConstructorFoundException? noMatchingConstructorFoundException = null; NoMatchingConstructorFoundException? noMatchingConstructorFoundException = null;
@ -548,7 +602,7 @@ namespace LightweightIocContainer
List<ConstructorInfo> sortedConstructors = TryGetSortedConstructors(type); List<ConstructorInfo> sortedConstructors = TryGetSortedConstructors(type);
foreach (ConstructorInfo constructor in sortedConstructors) foreach (ConstructorInfo constructor in sortedConstructors)
{ {
(bool result, List<object>? parameters, List<ConstructorNotMatchingException>? exceptions) = TryGetConstructorResolveStack(constructor, arguments, resolveStack); (bool result, List<object?>? parameters, List<ConstructorNotMatchingException>? exceptions) = TryGetConstructorResolveStack(constructor, arguments, resolveStack);
if (result) if (result)
return (true, parameters, null); return (true, parameters, null);
@ -572,14 +626,14 @@ namespace LightweightIocContainer
/// <para>parameters: The parameters needed to resolve the given <see cref="Type"/></para> /// <para>parameters: The parameters needed to resolve the given <see cref="Type"/></para>
/// <para>exception: A List of <see cref="ConstructorNotMatchingException"/>s if the constructor is not matching</para> /// <para>exception: A List of <see cref="ConstructorNotMatchingException"/>s if the constructor is not matching</para>
/// </returns> /// </returns>
private (bool result, List<object>? parameters, List<ConstructorNotMatchingException>? exceptions) TryGetConstructorResolveStack(ConstructorInfo constructor, object[]? arguments, List<Type> resolveStack) private (bool result, List<object?>? parameters, List<ConstructorNotMatchingException>? exceptions) TryGetConstructorResolveStack(ConstructorInfo constructor, object[]? arguments, List<Type> resolveStack)
{ {
List<ParameterInfo> constructorParameters = constructor.GetParameters().ToList(); List<ParameterInfo> constructorParameters = constructor.GetParameters().ToList();
if (!constructorParameters.Any()) if (!constructorParameters.Any())
return (true, null, null); return (true, null, null);
List<ConstructorNotMatchingException> exceptions = new(); List<ConstructorNotMatchingException> exceptions = new();
List<object> parameters = new(); List<object?> parameters = new();
List<object>? passedArguments = null; List<object>? passedArguments = null;
if (arguments != null) if (arguments != null)
@ -587,11 +641,11 @@ namespace LightweightIocContainer
foreach (ParameterInfo parameter in constructorParameters) foreach (ParameterInfo parameter in constructorParameters)
{ {
object fittingArgument = new InternalResolvePlaceholder(); object? fittingArgument = new InternalResolvePlaceholder();
if (passedArguments != null) if (passedArguments != null)
{ {
fittingArgument = passedArguments.FirstOrGiven<object, InternalResolvePlaceholder>(a => fittingArgument = passedArguments.FirstOrGiven<object, InternalResolvePlaceholder>(a =>
a?.GetType() == parameter.ParameterType || parameter.ParameterType.IsInstanceOfType(a)); a.GetType() == parameter.ParameterType || parameter.ParameterType.IsInstanceOfType(a));
if (fittingArgument is not InternalResolvePlaceholder) if (fittingArgument is not InternalResolvePlaceholder)
passedArguments.Remove(fittingArgument); passedArguments.Remove(fittingArgument);
@ -601,30 +655,7 @@ namespace LightweightIocContainer
{ {
try try
{ {
IRegistration registration = FindRegistration(parameter.ParameterType) ?? throw new TypeNotRegisteredException(parameter.ParameterType); fittingArgument = TryResolveNonGeneric(parameter.ParameterType, null, resolveStack);
List<Type> internalResolveStack = new(resolveStack);
internalResolveStack = CheckForCircularDependencies(parameter.ParameterType, internalResolveStack);
Type registeredType = GetTypeNonGeneric(parameter.ParameterType, registration);
object? singletonInstance = TryGetSingletonInstance(registeredType);
if (singletonInstance != null)
fittingArgument = singletonInstance;
else
{
object[]? argumentsForRegistration = null;
if (registration is IWithParametersInternal { Parameters: { } } registrationWithParameters)
argumentsForRegistration = UpdateArgumentsWithRegistrationParameters(registrationWithParameters, null);
(bool result, List<object>? parametersToResolve, NoMatchingConstructorFoundException? exception) =
TryGetTypeResolveStack(registeredType, argumentsForRegistration, internalResolveStack);
if (result)
fittingArgument = new InternalToBeResolvedPlaceholder(registeredType, parametersToResolve);
else if (exception != null)
exceptions.Add(new ConstructorNotMatchingException(constructor, exception));
}
} }
catch (Exception exception) catch (Exception exception)
{ {
@ -654,15 +685,9 @@ namespace LightweightIocContainer
/// </summary> /// </summary>
/// <typeparam name="T">The given <see cref="Type"/></typeparam> /// <typeparam name="T">The given <see cref="Type"/></typeparam>
/// <returns>The <see cref="IRegistration"/> for the given <see cref="Type"/></returns> /// <returns>The <see cref="IRegistration"/> for the given <see cref="Type"/></returns>
private IRegistration? FindRegistration<T>() => FindRegistration(typeof(T)); private IRegistration? FindRegistration<T>()
/// <summary>
/// Find the <see cref="IRegistration"/> for the given <see cref="Type"/>
/// </summary>
/// <param name="type">The given <see cref="Type"/></param>
/// <returns>The <see cref="IRegistration"/> for the given <see cref="Type"/></returns>
private IRegistration? FindRegistration(Type type)
{ {
Type type = typeof(T);
IRegistration? registration = Registrations.FirstOrDefault(r => r.InterfaceType == type); IRegistration? registration = Registrations.FirstOrDefault(r => r.InterfaceType == type);
if (registration != null) if (registration != null)
return registration; return registration;
@ -709,15 +734,6 @@ namespace LightweightIocContainer
_ => throw new UnknownRegistrationException($"Unknown registration used: {registration.GetType().Name}.") _ => throw new UnknownRegistrationException($"Unknown registration used: {registration.GetType().Name}.")
}; };
/// <summary>
/// Non generic method to get the implementation type for the given <see cref="IRegistration"/>
/// </summary>
/// <param name="type">The given <see cref="Type"/> of the interface</param>
/// <param name="registration">The given <see cref="IRegistration"/></param>
/// <returns>The implementation <see cref="Type"/> for the given <see cref="IRegistration"/></returns>
/// <exception cref="UnknownRegistrationException">Unknown <see cref="IRegistration"/> passed</exception>
private Type GetTypeNonGeneric(Type type, IRegistration registration) => (Type) GenericMethodCaller.CallPrivate(this, nameof(GetType), type, registration);
/// <summary> /// <summary>
/// Check the given resolve stack for circular dependencies /// Check the given resolve stack for circular dependencies
/// </summary> /// </summary>
@ -725,23 +741,14 @@ namespace LightweightIocContainer
/// <typeparam name="T">The given <see cref="Type"/></typeparam> /// <typeparam name="T">The given <see cref="Type"/></typeparam>
/// <returns>The new resolve stack</returns> /// <returns>The new resolve stack</returns>
/// <exception cref="CircularDependencyException">A circular dependency was detected</exception> /// <exception cref="CircularDependencyException">A circular dependency was detected</exception>
private List<Type> CheckForCircularDependencies<T>(List<Type>? resolveStack) => CheckForCircularDependencies(typeof(T), resolveStack); private List<Type> CheckForCircularDependencies<T>(List<Type>? resolveStack)
/// <summary>
/// Check the given resolve stack for circular dependencies
/// </summary>
/// <param name="type">The given <see cref="Type"/></param>
/// <param name="resolveStack">The given resolve stack</param>
/// <returns>The new resolve stack</returns>
/// <exception cref="CircularDependencyException">A circular dependency was detected</exception>
private List<Type> CheckForCircularDependencies(Type type, List<Type>? resolveStack)
{ {
if (resolveStack == null) //first resolve call if (resolveStack == null) //first resolve call
resolveStack = new List<Type> {type}; //create new stack and add the currently resolving type to the stack resolveStack = new List<Type> {typeof(T)}; //create new stack and add the currently resolving type to the stack
else if (resolveStack.Contains(type)) else if (resolveStack.Contains(typeof(T)))
throw new CircularDependencyException(type, resolveStack); //currently resolving type is still resolving -> circular dependency throw new CircularDependencyException(typeof(T), resolveStack); //currently resolving type is still resolving -> circular dependency
else //not the first resolve call in chain but no circular dependencies for now else //not the first resolve call in chain but no circular dependencies for now
resolveStack.Add(type); //add currently resolving type to the stack resolveStack.Add(typeof(T)); //add currently resolving type to the stack
return resolveStack; return resolveStack;
} }

@ -744,6 +744,11 @@
The <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistrationBase"/> that is used to register an Interface and extends the <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistration"/> with fluent options The <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistrationBase"/> that is used to register an Interface and extends the <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistration"/> with fluent options
</summary> </summary>
</member> </member>
<member name="T:LightweightIocContainer.Interfaces.Registrations.ISingleTypeRegistration">
<summary>
A base <see cref="T:LightweightIocContainer.Interfaces.Registrations.ISingleTypeRegistration`1"/> without generic interface
</summary>
</member>
<member name="T:LightweightIocContainer.Interfaces.Registrations.ISingleTypeRegistration`1"> <member name="T:LightweightIocContainer.Interfaces.Registrations.ISingleTypeRegistration`1">
<summary> <summary>
The <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistration"/> to register either only an interface or only a <see cref="T:System.Type"/> The <see cref="T:LightweightIocContainer.Interfaces.Registrations.IRegistration"/> to register either only an interface or only a <see cref="T:System.Type"/>
@ -1535,6 +1540,11 @@
<see cref="T:System.Func`2"/> that is invoked instead of creating an instance of this <see cref="T:System.Type"/> the default way <see cref="T:System.Func`2"/> that is invoked instead of creating an instance of this <see cref="T:System.Type"/> the default way
</summary> </summary>
</member> </member>
<member name="P:LightweightIocContainer.Registrations.SingleTypeRegistration`1.LightweightIocContainer#Interfaces#Registrations#ISingleTypeRegistration#HasFactoryMethod">
<summary>
True if <see cref="P:LightweightIocContainer.Registrations.SingleTypeRegistration`1.FactoryMethod"/> is set, false if not
</summary>
</member>
<member name="M:LightweightIocContainer.Registrations.SingleTypeRegistration`1.WithFactoryMethod(System.Func{LightweightIocContainer.Interfaces.IResolver,`0})"> <member name="M:LightweightIocContainer.Registrations.SingleTypeRegistration`1.WithFactoryMethod(System.Func{LightweightIocContainer.Interfaces.IResolver,`0})">
<summary> <summary>
Pass a <see cref="T:System.Func`2"/> that will be invoked instead of creating an instance of this <see cref="T:System.Type"/> the default way Pass a <see cref="T:System.Func`2"/> that will be invoked instead of creating an instance of this <see cref="T:System.Type"/> the default way
@ -1612,14 +1622,14 @@
An internal placeholder that is used to hold types that need to be resolved during the resolving process An internal placeholder that is used to hold types that need to be resolved during the resolving process
</summary> </summary>
</member> </member>
<member name="P:LightweightIocContainer.ResolvePlaceholders.InternalToBeResolvedPlaceholder.ResolvedType"> <member name="P:LightweightIocContainer.ResolvePlaceholders.InternalToBeResolvedPlaceholder.ResolvedRegistration">
<summary> <summary>
The <see cref="T:System.Type"/> to be resolved The <see cref="T:System.Type"/> to be resolved
</summary> </summary>
</member> </member>
<member name="P:LightweightIocContainer.ResolvePlaceholders.InternalToBeResolvedPlaceholder.Parameters"> <member name="P:LightweightIocContainer.ResolvePlaceholders.InternalToBeResolvedPlaceholder.Parameters">
<summary> <summary>
The parameters needed to resolve the <see cref="P:LightweightIocContainer.ResolvePlaceholders.InternalToBeResolvedPlaceholder.ResolvedType"/> The parameters needed to resolve the <see cref="P:LightweightIocContainer.ResolvePlaceholders.InternalToBeResolvedPlaceholder.ResolvedRegistration"/>
</summary> </summary>
</member> </member>
<member name="M:LightweightIocContainer.TypeExtension.GetDefault(System.Type)"> <member name="M:LightweightIocContainer.TypeExtension.GetDefault(System.Type)">

@ -0,0 +1,18 @@
// Author: Gockner, Simon
// Created: 2021-12-14
// Copyright(c) 2021 SimonG. All Rights Reserved.
using System;
namespace LightweightIocContainer.ResolvePlaceholders;
/// <summary>
/// An internal placeholder that is used to hold types that need to be resolved during the resolving process
/// </summary>
internal interface IInternalToBeResolvedPlaceholder
{
/// <summary>
/// The <see cref="Type"/> to be resolved
/// </summary>
Type ResolvedType { get; }
}

@ -0,0 +1,31 @@
// Author: Gockner, Simon
// Created: 2021-12-14
// Copyright(c) 2021 SimonG. All Rights Reserved.
using System;
using LightweightIocContainer.Interfaces;
using LightweightIocContainer.Interfaces.Registrations;
namespace LightweightIocContainer.ResolvePlaceholders;
/// <summary>
/// An internal placeholder that is used to hold factory methods for types that need to be resolved during the resolve process
/// </summary>
internal class InternalFactoryMethodPlaceholder<T> : IInternalToBeResolvedPlaceholder
{
public InternalFactoryMethodPlaceholder(ISingleTypeRegistration<T> singleTypeRegistration)
{
ResolvedType = singleTypeRegistration.InterfaceType;
FactoryMethod = singleTypeRegistration.FactoryMethod ?? throw new InvalidOperationException("Factory method can't be null.");
}
/// <summary>
/// The <see cref="Type"/> to be resolved
/// </summary>
public Type ResolvedType { get; }
/// <summary>
/// The <see cref="ISingleTypeRegistration{T}.FactoryMethod"/>
/// </summary>
public Func<IResolver, T> FactoryMethod { get; }
}

@ -4,17 +4,19 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using LightweightIocContainer.Interfaces.Registrations;
namespace LightweightIocContainer.ResolvePlaceholders namespace LightweightIocContainer.ResolvePlaceholders
{ {
/// <summary> /// <summary>
/// An internal placeholder that is used to hold types that need to be resolved during the resolving process /// An internal placeholder that is used to hold types that need to be resolved during the resolving process
/// </summary> /// </summary>
internal class InternalToBeResolvedPlaceholder internal class InternalToBeResolvedPlaceholder : IInternalToBeResolvedPlaceholder
{ {
public InternalToBeResolvedPlaceholder(Type resolvedType, List<object>? parameters) public InternalToBeResolvedPlaceholder(Type resolvedType, IRegistration resolvedRegistration, List<object?>? parameters)
{ {
ResolvedType = resolvedType; ResolvedType = resolvedType;
ResolvedRegistration = resolvedRegistration;
Parameters = parameters; Parameters = parameters;
} }
@ -24,8 +26,13 @@ namespace LightweightIocContainer.ResolvePlaceholders
public Type ResolvedType { get; } public Type ResolvedType { get; }
/// <summary> /// <summary>
/// The parameters needed to resolve the <see cref="ResolvedType"/> /// The <see cref="IRegistration"/> to be resolved
/// </summary> /// </summary>
public List<object>? Parameters { get; } public IRegistration ResolvedRegistration { get; }
/// <summary>
/// The parameters needed to resolve the <see cref="ResolvedRegistration"/>
/// </summary>
public List<object?>? Parameters { get; }
} }
} }
Loading…
Cancel
Save