diff --git a/LightweightIocContainer/GenericMethodCaller.cs b/LightweightIocContainer/GenericMethodCaller.cs
index bff301d..9663e34 100644
--- a/LightweightIocContainer/GenericMethodCaller.cs
+++ b/LightweightIocContainer/GenericMethodCaller.cs
@@ -41,5 +41,18 @@ namespace LightweightIocContainer
throw ex.GetBaseException();
}
}
+
+ ///
+ /// Call a private generic method without generic type parameters
+ ///
+ /// The caller of the method
+ /// The name of the method to call
+ /// The generic parameter as parameter
+ /// The parameters of the method
+ /// The result of invoking the method
+ /// Could not find the generic method
+ /// Any thrown after invoking the generic method
+ public static object CallPrivate(object caller, string functionName, Type genericParameter, params object[] parameters) =>
+ Call(caller, functionName, genericParameter, BindingFlags.NonPublic | BindingFlags.Instance, parameters);
}
}
\ No newline at end of file
diff --git a/LightweightIocContainer/IocContainer.cs b/LightweightIocContainer/IocContainer.cs
index d583ca8..50bb495 100644
--- a/LightweightIocContainer/IocContainer.cs
+++ b/LightweightIocContainer/IocContainer.cs
@@ -15,6 +15,7 @@ using LightweightIocContainer.Interfaces.Installers;
using LightweightIocContainer.Interfaces.Registrations;
using LightweightIocContainer.Interfaces.Registrations.Fluent;
using LightweightIocContainer.Registrations;
+using LightweightIocContainer.ResolvePlaceholders;
namespace LightweightIocContainer
{
@@ -266,8 +267,25 @@ namespace LightweightIocContainer
/// An instance of the given
/// Could not find function
internal object Resolve(Type type, object[] arguments, List resolveStack) =>
- GenericMethodCaller.Call(this, nameof(ResolveInternal), type, BindingFlags.NonPublic | BindingFlags.Instance, arguments, resolveStack);
+ GenericMethodCaller.CallPrivate(this, nameof(ResolveInternal), type, arguments, resolveStack);
+ private object Resolve(InternalToBeResolvedPlaceholder toBeResolvedPlaceholder, List resolveStack)
+ {
+ if (toBeResolvedPlaceholder.Parameters == null)
+ return Resolve(toBeResolvedPlaceholder.ResolvedType, null, resolveStack);
+
+ List parameters = new();
+ foreach (object parameter in toBeResolvedPlaceholder.Parameters)
+ {
+ if (parameter is InternalToBeResolvedPlaceholder internalToBeResolvedPlaceholder)
+ parameters.Add(Resolve(internalToBeResolvedPlaceholder, resolveStack));
+ else
+ parameters.Add(parameter);
+ }
+
+ return Resolve(toBeResolvedPlaceholder.ResolvedType, parameters.ToArray(), resolveStack);
+ }
+
///
/// Gets an instance of a given registered
///
@@ -282,12 +300,7 @@ namespace LightweightIocContainer
IRegistration registration = FindRegistration() ?? throw new TypeNotRegisteredException(typeof(T));
//Circular dependency check
- if (resolveStack == null) //first resolve call
- resolveStack = new List {typeof(T)}; //create new stack and add the currently resolving type to the stack
- else if (resolveStack.Contains(typeof(T)))
- 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
- resolveStack.Add(typeof(T)); //add currently resolving type to the stack
+ resolveStack = CheckForCircularDependencies(resolveStack);
T resolvedInstance = registration switch
{
@@ -313,15 +326,9 @@ namespace LightweightIocContainer
/// An existing or newly created singleton instance of the given
private T GetOrCreateSingletonInstance(IRegistration registration, object[] arguments, List resolveStack)
{
- Type type = registration switch
- {
- ITypedRegistration typedRegistration => typedRegistration.ImplementationType,
- ISingleTypeRegistration singleTypeRegistration => singleTypeRegistration.InterfaceType,
- _ => throw new UnknownRegistrationException($"There is no registration {registration.GetType().Name} that can have lifestyle singleton.")
- };
-
- //if a singleton instance exists return it
- object instance = _singletons.FirstOrDefault(s => s.type == type).instance;
+ Type type = GetType(registration);
+
+ object instance = TryGetSingletonInstance(type);
if (instance != null)
return (T) instance;
@@ -331,6 +338,8 @@ namespace LightweightIocContainer
return newInstance;
}
+
+ private object TryGetSingletonInstance(Type type) => _singletons.FirstOrDefault(s => s.type == type).instance; //if a singleton instance exists return it
///
/// Gets or creates a multiton instance of a given
@@ -390,7 +399,7 @@ namespace LightweightIocContainer
T instance;
if (registration is IOpenGenericRegistration openGenericRegistration)
{
- arguments = ResolveConstructorArguments(openGenericRegistration.ImplementationType, arguments, resolveStack);
+ arguments = ResolveTypeCreationArguments(openGenericRegistration.ImplementationType, arguments, resolveStack);
//create generic implementation type from generic arguments of T
Type genericImplementationType = openGenericRegistration.ImplementationType.MakeGenericType(typeof(T).GenericTypeArguments);
@@ -399,7 +408,7 @@ namespace LightweightIocContainer
}
else if (registration is ITypedRegistration defaultRegistration)
{
- arguments = ResolveConstructorArguments(defaultRegistration.ImplementationType, arguments, resolveStack);
+ arguments = ResolveTypeCreationArguments(defaultRegistration.ImplementationType, arguments, resolveStack);
instance = (T) Activator.CreateInstance(defaultRegistration.ImplementationType, arguments);
}
else if (registration is ISingleTypeRegistration singleTypeRegistration)
@@ -409,7 +418,7 @@ namespace LightweightIocContainer
if (singleTypeRegistration.FactoryMethod == null) //type registration without interface -> just create this type
{
- arguments = ResolveConstructorArguments(singleTypeRegistration.InterfaceType, arguments, resolveStack);
+ arguments = ResolveTypeCreationArguments(singleTypeRegistration.InterfaceType, arguments, resolveStack);
instance = (T)Activator.CreateInstance(singleTypeRegistration.InterfaceType, arguments);
}
else //factory method set to create the instance
@@ -468,7 +477,7 @@ namespace LightweightIocContainer
}
///
- /// Resolve the missing constructor arguments
+ /// Resolve the missing type creation arguments
///
/// The that will be created
/// The existing arguments
@@ -476,101 +485,187 @@ namespace LightweightIocContainer
/// An array of all needed constructor arguments to create the
/// No matching constructor was found for the given or resolvable arguments
[CanBeNull]
- private object[] ResolveConstructorArguments(Type type, object[] arguments, List resolveStack)
+ private object[] ResolveTypeCreationArguments(Type type, object[] arguments, List resolveStack)
{
+ NoMatchingConstructorFoundException noMatchingConstructorFoundException = null;
+
//find best ctor
- List sortedConstructors = type.GetConstructors().OrderByDescending(c => c.GetParameters().Length).ToList();
- if (!sortedConstructors.Any()) //no public constructor available
- throw new NoPublicConstructorFoundException(type);
+ List sortedConstructors = TryGetSortedConstructors(type);
+ foreach (ConstructorInfo constructor in sortedConstructors)
+ {
+ (bool result, List parameters, List exceptions) = TryGetConstructorResolveStack(constructor, arguments, resolveStack);
- NoMatchingConstructorFoundException noMatchingConstructorFoundException = null;
+ if (result)
+ {
+ if (parameters == null)
+ return null;
+
+ List constructorParameters = new();
+ foreach (object parameter in parameters)
+ {
+ if (parameter is InternalToBeResolvedPlaceholder toBeResolvedPlaceholder)
+ constructorParameters.Add(Resolve(toBeResolvedPlaceholder, resolveStack));
+ else
+ constructorParameters.Add(parameter);
+ }
+
+ return constructorParameters.ToArray();
+ }
+
+ noMatchingConstructorFoundException ??= new NoMatchingConstructorFoundException(type);
+ exceptions.ForEach(e =>
+ noMatchingConstructorFoundException.AddInnerException(new ConstructorNotMatchingException(constructor, e)));
+ }
+
+ if (noMatchingConstructorFoundException != null)
+ throw noMatchingConstructorFoundException;
+
+ return null;
+ }
- foreach (ConstructorInfo ctor in sortedConstructors)
+ private (bool result, List parameters, List constructorNotMatchingExceptions) TryGetConstructorResolveStack(ConstructorInfo constructor, object[] arguments, List resolveStack)
+ {
+ List constructorParameters = constructor.GetParameters().ToList();
+ if (!constructorParameters.Any())
+ return (true, null, null);
+
+ List constructorNotMatchingExceptions = new();
+ List parameters = new();
+
+ List passedArguments = null;
+ if (arguments != null)
+ passedArguments = new List(arguments);
+
+ foreach (ParameterInfo parameter in constructorParameters)
{
- try
+ object fittingArgument = new InternalResolvePlaceholder();
+ if (passedArguments != null)
{
- List argumentsList = arguments?.ToList();
- List ctorParams = new();
+ fittingArgument = passedArguments.FirstOrGiven(a =>
+ a?.GetType() == parameter.ParameterType || parameter.ParameterType.IsInstanceOfType(a));
+
+ if (fittingArgument is not InternalResolvePlaceholder)
+ {
+ int index = passedArguments.IndexOf(fittingArgument); //todo
+ passedArguments[index] = new InternalResolvePlaceholder();
+ }
+ }
- ParameterInfo[] parameters = ctor.GetParameters();
- foreach (ParameterInfo parameter in parameters)
+ if (fittingArgument is InternalResolvePlaceholder)
+ {
+ try
{
- object fittingArgument = new InternalResolvePlaceholder();
- if (argumentsList != null)
+ IRegistration registration = FindRegistration(parameter.ParameterType) ?? throw new TypeNotRegisteredException(parameter.ParameterType);
+
+ List internalResolveStack = new(resolveStack);
+ internalResolveStack = CheckForCircularDependencies(parameter.ParameterType, internalResolveStack); //testMe: seems to work
+
+ Type registeredType = GetTypeNonGeneric(parameter.ParameterType, registration);
+
+ object singletonInstance = TryGetSingletonInstance(registeredType);
+ if (singletonInstance != null)
+ fittingArgument = singletonInstance;
+ else
{
- fittingArgument = argumentsList.FirstOrGiven(a =>
- a?.GetType() == parameter.ParameterType || parameter.ParameterType.IsInstanceOfType(a));
-
- if (fittingArgument is not InternalResolvePlaceholder)
- {
- int index = argumentsList.IndexOf(fittingArgument);
- argumentsList[index] = new InternalResolvePlaceholder();
- }
- else //fittingArgument is InternalResolvePlaceholder
+ object[] argumentsForRegistration = null;
+ if (registration is IWithParametersInternal { Parameters: { } } registrationWithParameters)
+ argumentsForRegistration = UpdateArgumentsWithRegistrationParameters(registrationWithParameters, null);
+
+ foreach (ConstructorInfo registeredTypeConstructor in TryGetSortedConstructors(registeredType))
{
- try
+ (bool result, List parametersToResolve, List exceptions) =
+ TryGetConstructorResolveStack(registeredTypeConstructor, argumentsForRegistration, internalResolveStack);
+
+ if (result)
{
- fittingArgument = Resolve(parameter.ParameterType, null, resolveStack);
+ fittingArgument = new InternalToBeResolvedPlaceholder(registeredType, parametersToResolve);
+ break;
}
- catch (Exception)
- {
- fittingArgument = argumentsList.FirstOrGiven(a => parameter.ParameterType.GetDefault() == a);
- if (fittingArgument is not InternalResolvePlaceholder)
- {
- int index = argumentsList.IndexOf(fittingArgument);
- argumentsList[index] = new InternalResolvePlaceholder();
- }
- }
+ constructorNotMatchingExceptions.AddRange(exceptions);
}
}
-
- if (fittingArgument is InternalResolvePlaceholder && parameter.HasDefaultValue)
- ctorParams.Add(parameter.DefaultValue);
- else if (fittingArgument is InternalResolvePlaceholder)
- ctorParams.Add(Resolve(parameter.ParameterType, null, resolveStack));
- else
- ctorParams.Add(fittingArgument);
+ }
+ catch (Exception exception)
+ {
+ constructorNotMatchingExceptions.Add(new ConstructorNotMatchingException(constructor, exception));
}
- return ctorParams.ToArray();
- }
- catch (CircularDependencyException) //don't handle circular dependencies as no matching constructor, just rethrow them
- {
- throw;
- }
- catch (Exception ex)
- {
- noMatchingConstructorFoundException ??= new NoMatchingConstructorFoundException(type);
- noMatchingConstructorFoundException.AddInnerException(new ConstructorNotMatchingException(ctor, ex));
+ if (fittingArgument is InternalResolvePlaceholder && passedArguments != null)
+ {
+ fittingArgument = passedArguments.FirstOrGiven(a => parameter.ParameterType.GetDefault() == a);
+
+ if (fittingArgument is not InternalResolvePlaceholder)
+ {
+ int index = passedArguments.IndexOf(fittingArgument);
+ passedArguments[index] = new InternalResolvePlaceholder();
+ }
+ }
}
+
+ if (fittingArgument is InternalResolvePlaceholder && parameter.HasDefaultValue)
+ parameters.Add(parameter.DefaultValue);
+ else
+ parameters.Add(fittingArgument);
}
- if (noMatchingConstructorFoundException != null)
- throw noMatchingConstructorFoundException;
-
- return null;
+ return (!parameters.Any(p => p is InternalResolvePlaceholder), parameters, constructorNotMatchingExceptions);
}
[CanBeNull]
- private IRegistration FindRegistration()
+ private IRegistration FindRegistration() => FindRegistration(typeof(T));
+
+ [CanBeNull]
+ private IRegistration FindRegistration(Type type)
{
- IRegistration registration = Registrations.FirstOrDefault(r => r.InterfaceType == typeof(T));
+ IRegistration registration = Registrations.FirstOrDefault(r => r.InterfaceType == type);
if (registration != null)
return registration;
- registration = Registrations.OfType().FirstOrDefault(r => r.ImplementationType == typeof(T));
+ registration = Registrations.OfType().FirstOrDefault(r => r.ImplementationType == type);
if (registration != null)
return registration;
//check for open generic registration
- if (!typeof(T).GenericTypeArguments.Any())
+ if (!type.GenericTypeArguments.Any())
return null;
List openGenericRegistrations = Registrations.Where(r => r.InterfaceType.ContainsGenericParameters).ToList();
- return !openGenericRegistrations.Any() ? null : openGenericRegistrations.FirstOrDefault(r => r.InterfaceType == typeof(T).GetGenericTypeDefinition());
+ return !openGenericRegistrations.Any() ? null : openGenericRegistrations.FirstOrDefault(r => r.InterfaceType == type.GetGenericTypeDefinition());
+ }
+
+ private List TryGetSortedConstructors(Type type)
+ {
+ List sortedConstructors = type.GetConstructors().OrderByDescending(c => c.GetParameters().Length).ToList();
+ if (!sortedConstructors.Any()) //no public constructor available
+ throw new NoPublicConstructorFoundException(type);
+
+ return sortedConstructors;
}
+ private Type GetType(IRegistration registration) =>
+ registration switch
+ {
+ ITypedRegistration typedRegistration => typedRegistration.ImplementationType,
+ ISingleTypeRegistration singleTypeRegistration => singleTypeRegistration.InterfaceType,
+ _ => throw new UnknownRegistrationException($"Unknown registration used: {registration.GetType().Name}.")
+ };
+
+ private Type GetTypeNonGeneric(Type type, IRegistration registration) => (Type) GenericMethodCaller.CallPrivate(this, nameof(GetType), type, registration);
+
+ private List CheckForCircularDependencies(List resolveStack) => CheckForCircularDependencies(typeof(T), resolveStack);
+ private List CheckForCircularDependencies(Type type, List resolveStack)
+ {
+ if (resolveStack == null) //first resolve call
+ resolveStack = new List {type}; //create new stack and add the currently resolving type to the stack
+ else if (resolveStack.Contains(type))
+ throw new CircularDependencyException(type, resolveStack); //currently resolving type is still resolving -> circular dependency
+ else //not the first resolve call in chain but no circular dependencies for now
+ resolveStack.Add(type); //add currently resolving type to the stack
+
+ return resolveStack;
+ }
+
///
/// Clear the multiton instances of the given from the registered multitons list
///
diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml
index 7be92cf..e656be4 100644
--- a/LightweightIocContainer/LightweightIocContainer.xml
+++ b/LightweightIocContainer/LightweightIocContainer.xml
@@ -331,6 +331,18 @@
Could not find the generic method
Any thrown after invoking the generic method
+
+
+ Call a private generic method without generic type parameters
+
+ The caller of the method
+ The name of the method to call
+ The generic parameter as parameter
+ The parameters of the method
+ The result of invoking the method
+ Could not find the generic method
+ Any thrown after invoking the generic method
+
An that installs all s for its given
@@ -777,11 +789,6 @@
A that implements a
-
-
- An internal placeholder that is used during the resolving process
-
-
The main container that carries all the s and can resolve all the types you'll ever want
@@ -990,9 +997,9 @@
The constructor arguments
The argument list updated with the
-
+
- Resolve the missing constructor arguments
+ Resolve the missing type creation arguments
The that will be created
The existing arguments
@@ -1497,6 +1504,16 @@
The
The current instance of this
+
+
+ An internal placeholder that is used during the resolving process
+
+
+
+
+ An internal placeholder that is used to hold types that need to be resolved during the resolving process
+
+
Returns the default value for a given
diff --git a/LightweightIocContainer/Registrations/RegistrationBase.cs b/LightweightIocContainer/Registrations/RegistrationBase.cs
index a57a28b..63481e6 100644
--- a/LightweightIocContainer/Registrations/RegistrationBase.cs
+++ b/LightweightIocContainer/Registrations/RegistrationBase.cs
@@ -10,6 +10,7 @@ using LightweightIocContainer.Interfaces.Factories;
using LightweightIocContainer.Interfaces.Installers;
using LightweightIocContainer.Interfaces.Registrations;
using LightweightIocContainer.Interfaces.Registrations.Fluent;
+using LightweightIocContainer.ResolvePlaceholders;
namespace LightweightIocContainer.Registrations
{
diff --git a/LightweightIocContainer/InternalResolvePlaceholder.cs b/LightweightIocContainer/ResolvePlaceholders/InternalResolvePlaceholder.cs
similarity index 83%
rename from LightweightIocContainer/InternalResolvePlaceholder.cs
rename to LightweightIocContainer/ResolvePlaceholders/InternalResolvePlaceholder.cs
index 8f97059..4bfc7e9 100644
--- a/LightweightIocContainer/InternalResolvePlaceholder.cs
+++ b/LightweightIocContainer/ResolvePlaceholders/InternalResolvePlaceholder.cs
@@ -2,7 +2,7 @@
// Created: 2019-11-22
// Copyright(c) 2019 SimonG. All Rights Reserved.
-namespace LightweightIocContainer
+namespace LightweightIocContainer.ResolvePlaceholders
{
///
/// An internal placeholder that is used during the resolving process
diff --git a/LightweightIocContainer/ResolvePlaceholders/InternalToBeResolvedPlaceholder.cs b/LightweightIocContainer/ResolvePlaceholders/InternalToBeResolvedPlaceholder.cs
new file mode 100644
index 0000000..73d819a
--- /dev/null
+++ b/LightweightIocContainer/ResolvePlaceholders/InternalToBeResolvedPlaceholder.cs
@@ -0,0 +1,24 @@
+// Author: Gockner, Simon
+// Created: 2021-12-08
+// Copyright(c) 2021 SimonG. All Rights Reserved.
+
+using System;
+using System.Collections.Generic;
+
+namespace LightweightIocContainer.ResolvePlaceholders
+{
+ ///
+ /// An internal placeholder that is used to hold types that need to be resolved during the resolving process
+ ///
+ internal class InternalToBeResolvedPlaceholder
+ {
+ public InternalToBeResolvedPlaceholder(Type resolvedType, List parameters)
+ {
+ ResolvedType = resolvedType;
+ Parameters = parameters;
+ }
+
+ public Type ResolvedType { get; }
+ public List Parameters { get; }
+ }
+}
\ No newline at end of file
diff --git a/Test.LightweightIocContainer/RegistrationBaseTest.cs b/Test.LightweightIocContainer/RegistrationBaseTest.cs
index 81cc84a..6776230 100644
--- a/Test.LightweightIocContainer/RegistrationBaseTest.cs
+++ b/Test.LightweightIocContainer/RegistrationBaseTest.cs
@@ -6,6 +6,7 @@ using JetBrains.Annotations;
using LightweightIocContainer;
using LightweightIocContainer.Exceptions;
using LightweightIocContainer.Registrations;
+using LightweightIocContainer.ResolvePlaceholders;
using Moq;
using NUnit.Framework;