From 3b11e24e7b8f32e2b33ef3de92b94220896d6964 Mon Sep 17 00:00:00 2001 From: Simon G Date: Tue, 14 Dec 2021 16:25:50 +0100 Subject: [PATCH] #51: completely rework resolve - introduce new method TryResolve that is used by internalResolve and tryGetConstructorResolveStack - add own creator helper class - fix nullable warnings --- LightweightIocContainer/Creator.cs | 24 + .../Factories/TypedFactory.cs | 8 +- .../GenericMethodCaller.cs | 4 +- .../Installers/AssemblyInstaller.cs | 2 +- LightweightIocContainer/IocContainer.cs | 441 +++++++++--------- .../LightweightIocContainer.xml | 14 +- .../IInternalToBeResolvedPlaceholder.cs | 18 + .../InternalFactoryMethodPlaceholder.cs | 31 ++ .../InternalToBeResolvedPlaceholder.cs | 15 +- 9 files changed, 327 insertions(+), 230 deletions(-) create mode 100644 LightweightIocContainer/Creator.cs create mode 100644 LightweightIocContainer/ResolvePlaceholders/IInternalToBeResolvedPlaceholder.cs create mode 100644 LightweightIocContainer/ResolvePlaceholders/InternalFactoryMethodPlaceholder.cs diff --git a/LightweightIocContainer/Creator.cs b/LightweightIocContainer/Creator.cs new file mode 100644 index 0000000..89daa6c --- /dev/null +++ b/LightweightIocContainer/Creator.cs @@ -0,0 +1,24 @@ +// Author: Gockner, Simon +// Created: 2021-12-14 +// Copyright(c) 2021 SimonG. All Rights Reserved. + +using System; + +namespace LightweightIocContainer; + +/// +/// Helper for dynamic instance creation +/// +internal static class Creator +{ + /// + /// Creates an instance of the given with the given arguments + /// + /// The given + /// The given arguments + /// The that is returned + /// A new instance of the given + /// The given type could not be created + public static T CreateInstance(Type type, params object?[]? arguments) => + (T) (Activator.CreateInstance(type, arguments) ?? throw new InvalidOperationException($"Type {type} could not be created.")); +} \ No newline at end of file diff --git a/LightweightIocContainer/Factories/TypedFactory.cs b/LightweightIocContainer/Factories/TypedFactory.cs index 88df1dd..e4f4e0b 100644 --- a/LightweightIocContainer/Factories/TypedFactory.cs +++ b/LightweightIocContainer/Factories/TypedFactory.cs @@ -92,11 +92,11 @@ namespace LightweightIocContainer.Factories } 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.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.Ret); } @@ -127,7 +127,7 @@ namespace LightweightIocContainer.Factories multitonClearGenerator.Emit(OpCodes.Ldarg_0); 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); } else @@ -136,7 +136,7 @@ namespace LightweightIocContainer.Factories } } - return (TFactory) Activator.CreateInstance(typeBuilder.CreateTypeInfo()?.AsType(), container); + return Creator.CreateInstance(typeBuilder.CreateTypeInfo()!.AsType(), container); } } } \ No newline at end of file diff --git a/LightweightIocContainer/GenericMethodCaller.cs b/LightweightIocContainer/GenericMethodCaller.cs index 8052c37..41c1da2 100644 --- a/LightweightIocContainer/GenericMethodCaller.cs +++ b/LightweightIocContainer/GenericMethodCaller.cs @@ -24,7 +24,7 @@ namespace LightweightIocContainer /// The result of invoking the method /// Could not find the generic method /// Any thrown after invoking the generic method - 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? genericMethod = method?.MakeGenericMethod(genericParameter); @@ -52,7 +52,7 @@ namespace LightweightIocContainer /// 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) => + 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/Installers/AssemblyInstaller.cs b/LightweightIocContainer/Installers/AssemblyInstaller.cs index af63a20..9fc8c0d 100644 --- a/LightweightIocContainer/Installers/AssemblyInstaller.cs +++ b/LightweightIocContainer/Installers/AssemblyInstaller.cs @@ -29,7 +29,7 @@ namespace LightweightIocContainer.Installers if (!typeof(IIocInstaller).IsAssignableFrom(type) || type.IsNestedPrivate) continue; - Installers.Add((IIocInstaller) Activator.CreateInstance(type)); + Installers.Add(Creator.CreateInstance(type)); } } diff --git a/LightweightIocContainer/IocContainer.cs b/LightweightIocContainer/IocContainer.cs index 881d037..5af49ed 100644 --- a/LightweightIocContainer/IocContainer.cs +++ b/LightweightIocContainer/IocContainer.cs @@ -270,182 +270,271 @@ namespace LightweightIocContainer /// The current resolve stack /// An instance of the given /// Could not find function - internal object Resolve(Type type, object?[]? arguments, List? resolveStack) => + internal object? Resolve(Type type, object?[]? arguments, List? resolveStack) => GenericMethodCaller.CallPrivate(this, nameof(ResolveInternal), type, arguments, resolveStack); + /// + /// Gets an instance of a given registered + /// + /// The registered + /// The constructor arguments + /// The current resolve stack + /// An instance of the given registered + private T ResolveInternal(object[]? arguments, List? resolveStack = null) => ResolveInstance(TryResolve(arguments, resolveStack)); + + /// + /// Tries to resolve the given with the given arguments + /// + /// The given arguments + /// The current resolve stack + /// The registered + /// An instance of the given registered , an if parameters need to be resolved or an if a factory method is used to create an instance + /// The given is not registered + /// An interface was registered without an implementation or factory method + /// Tried resolving a multiton without scope argument + /// No matching constructor for the given found + /// Getting resolve stack failed without exception + private object TryResolve(object[]? arguments, List? resolveStack) + { + IRegistration registration = FindRegistration() ?? throw new TypeNotRegisteredException(typeof(T)); + + List internalResolveStack = resolveStack == null ? new List() : new List(resolveStack); + internalResolveStack = CheckForCircularDependencies(internalResolveStack); + + object? existingInstance = TryGetExistingInstance(registration, arguments); + if (existingInstance != null) + return existingInstance; + + if (registration is ISingleTypeRegistration 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(singleTypeRegistration); + } + + if (registration is IWithParametersInternal { Parameters: { } } registrationWithParameters) + arguments = UpdateArgumentsWithRegistrationParameters(registrationWithParameters, arguments); + + Type registeredType = GetType(registration); + (bool result, List? 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(); + 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."); + } + + /// + /// Tries to resolve the given with the given arguments without generic arguments + /// + /// The registered + /// The given arguments + /// The current resolve stack + /// An instance of the given registered , an if parameters need to be resolved or an if a factory method is used to create an instance + /// The given is not registered + /// An interface was registered without an implementation or factory method + /// Tried resolving a multiton without scope argument + /// No matching constructor for the given found + /// Getting resolve stack failed without exception + private object? TryResolveNonGeneric(Type type, object[]? arguments, List resolveStack) => + GenericMethodCaller.CallPrivate(this, nameof(TryResolve), type, arguments, resolveStack); + /// /// Recursively resolve a with the given parameters for an /// /// The that includes the type and resolve stack - /// The current resolve stack /// A recursively resolved instance of the given - private object Resolve(InternalToBeResolvedPlaceholder toBeResolvedPlaceholder, List resolveStack) + private T ResolvePlaceholder(InternalToBeResolvedPlaceholder toBeResolvedPlaceholder) { if (toBeResolvedPlaceholder.Parameters == null) - return Resolve(toBeResolvedPlaceholder.ResolvedType, null, resolveStack); + return CreateInstance(toBeResolvedPlaceholder.ResolvedRegistration, null); - List parameters = new(); - foreach (object parameter in toBeResolvedPlaceholder.Parameters) + List parameters = new(); + foreach (object? parameter in toBeResolvedPlaceholder.Parameters) { - if (parameter is InternalToBeResolvedPlaceholder internalToBeResolvedPlaceholder) - parameters.Add(Resolve(internalToBeResolvedPlaceholder, resolveStack)); + if (parameter != null) + { + Type type = parameter is IInternalToBeResolvedPlaceholder internalToBeResolvedPlaceholder ? + internalToBeResolvedPlaceholder.ResolvedType : parameter.GetType(); + + parameters.Add(ResolveInstanceNonGeneric(type, parameter)); + } else parameters.Add(parameter); } - return Resolve(toBeResolvedPlaceholder.ResolvedType, parameters.ToArray(), resolveStack); + return CreateInstance(toBeResolvedPlaceholder.ResolvedRegistration, parameters.ToArray()); } - + /// - /// Gets an instance of a given registered + /// Resolve the given object instance /// - /// The registered - /// The constructor arguments - /// The current resolve stack - /// An instance of the given registered - /// The given is not registered in this - /// The registration for the given has an unknown - private T ResolveInternal(object[]? arguments, List? resolveStack = null) - { - IRegistration registration = FindRegistration() ?? throw new TypeNotRegisteredException(typeof(T)); - - //Circular dependency check - resolveStack = CheckForCircularDependencies(resolveStack); - - T resolvedInstance = registration switch + /// The given resolved object + /// The of the returned instance + /// An instance of the given resolved object + /// Resolve returned wrong type + private T ResolveInstance(object resolvedObject) => + resolvedObject switch { - RegistrationBase { Lifestyle: Lifestyle.Singleton } defaultRegistration => GetOrCreateSingletonInstance(defaultRegistration, arguments, resolveStack), - RegistrationBase { Lifestyle: Lifestyle.Multiton } and IMultitonRegistration multitonRegistration => GetOrCreateMultitonInstance(multitonRegistration, arguments, resolveStack), - RegistrationBase defaultRegistration => CreateInstance(defaultRegistration, arguments, resolveStack), - ITypedFactoryRegistration typedFactoryRegistration => typedFactoryRegistration.Factory.Factory, - _ => throw new UnknownRegistrationException($"There is no registration of type {registration.GetType().Name}.") + T instance => instance, + InternalToBeResolvedPlaceholder toBeResolvedPlaceholder => ResolvePlaceholder(toBeResolvedPlaceholder), + InternalFactoryMethodPlaceholder factoryMethodPlaceholder => factoryMethodPlaceholder.FactoryMethod(this), + _ => throw new InternalResolveException("Resolve returned wrong type.") }; - resolveStack.Remove(typeof(T)); //T was successfully resolved -> no circular dependency -> remove from resolve stack - - return resolvedInstance; - } + /// + /// Resolve the given object instance without generic arguments + /// + /// The of the returned instance + /// + /// An instance of the given resolved object + /// Resolve returned wrong type + private object? ResolveInstanceNonGeneric(Type type, object resolveObject) => + GenericMethodCaller.CallPrivate(this, nameof(ResolveInstance), type, resolveObject); /// - /// Gets or creates a singleton instance of a given + /// Creates an instance of a given /// /// The given /// The registration of the given - /// The arguments to resolve - /// The current resolve stack - /// An existing or newly created singleton instance of the given - private T GetOrCreateSingletonInstance(IRegistration registration, object[]? arguments, List resolveStack) + /// The constructor arguments + /// A newly created instance of the given + private T CreateInstance(IRegistration registration, object?[]? arguments) { - Type type = GetType(registration); - - object? instance = TryGetSingletonInstance(type); - if (instance != null) - return (T) instance; + T instance; + if (registration is IOpenGenericRegistration openGenericRegistration) + { + //create generic implementation type from generic arguments of T + Type genericImplementationType = openGenericRegistration.ImplementationType.MakeGenericType(typeof(T).GenericTypeArguments); + instance = Creator.CreateInstance(genericImplementationType, arguments); + } + else if (registration is ISingleTypeRegistration singleTypeRegistration) + instance = singleTypeRegistration.FactoryMethod == null ? Creator.CreateInstance(singleTypeRegistration.InterfaceType, arguments) : singleTypeRegistration.FactoryMethod(this); + else if (registration is ILifestyleProvider { Lifestyle: Lifestyle.Multiton } and IMultitonRegistration multitonRegistration) + instance = CreateMultitonInstance(multitonRegistration, arguments); + else if (registration is ITypedRegistration defaultRegistration) + instance = Creator.CreateInstance(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 - T newInstance = CreateInstance(registration, arguments, resolveStack); - _singletons.Add((type, newInstance)); + if (registration is ILifestyleProvider { Lifestyle: Lifestyle.Singleton }) + _singletons.Add((GetType(registration), instance)); - return newInstance; + if (registration is IOnCreate onCreateRegistration) + onCreateRegistration.OnCreateAction?.Invoke(instance); //TODO: Allow async OnCreateAction? + + return instance; } /// - /// Try to get an existing singleton instance for a given + /// Try to get an already existing instance (factory, singleton, multiton) /// - /// The given - /// A singleton instance if existing for the given , null if not - private object? TryGetSingletonInstance(Type type) => _singletons.FirstOrDefault(s => s.type == type).instance; //if a singleton instance exists return it + /// The given + /// The given arguments + /// The of the instance + /// An already existing instance if possible, null if not + private object? TryGetExistingInstance(IRegistration registration, IReadOnlyList? arguments) => + registration switch + { + ITypedFactoryRegistration typedFactoryRegistration => typedFactoryRegistration.Factory.Factory, + ILifestyleProvider { Lifestyle: Lifestyle.Singleton } => TryGetSingletonInstance(registration), + ILifestyleProvider { Lifestyle: Lifestyle.Multiton } and IMultitonRegistration multitonRegistration => TryGetMultitonInstance(multitonRegistration, arguments), + _ => null + }; + + /// + /// Try to get an existing singleton instance for a given + /// + /// The + /// A singleton instance if existing for the given , null if not + private object? TryGetSingletonInstance(IRegistration registration) => + _singletons.FirstOrDefault(s => s.type == GetType(registration)).instance; //if a singleton instance exists return it /// - /// Gets or creates a multiton instance of a given + /// Try to get an existing multiton instance for a given /// - /// The given - /// The registration of the given - /// The arguments to resolve - /// The current resolve stack - /// An existing or newly created multiton instance of the given - /// No arguments given - /// Scope argument not given - private T GetOrCreateMultitonInstance(IMultitonRegistration registration, object[]? arguments, List resolveStack) + /// The given + /// The given arguments + /// A multiton instance if existing for the given , null if not + /// Tried resolving a multiton without scope argument + private object? TryGetMultitonInstance(IMultitonRegistration registration, IReadOnlyList? arguments) { if (arguments == null || !arguments.Any()) - throw new MultitonResolveException("Can not resolve multiton without arguments.", typeof(T)); - - 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}).", typeof(T)); + throw new MultitonResolveException("Can not resolve multiton without arguments.", registration.InterfaceType); + + object scopeArgument = TryGetMultitonScopeArgument(registration, arguments); //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) - if (instances != null) - { - if (instances.TryGetValue(scopeArgument, out object? instance) && instance != null) - return (T) instance; - - T createdInstance = CreateInstance(registration, arguments, resolveStack); - instances.Add(scopeArgument, createdInstance); - - return createdInstance; - } + 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) + return null; - T newInstance = CreateInstance(registration, arguments, resolveStack); + return matchingMultitons.instances.TryGetValue(scopeArgument, out object? instance) && instance != null ? instance : null; + } - ConditionalWeakTable weakTable = new(); - weakTable.Add(scopeArgument, newInstance); + /// + /// Try to get the multiton scope argument for a given + /// + /// The given + /// The given arguments + /// The multiton scope argument for the given + /// Tried resolving a multiton without correct scope argument + private object TryGetMultitonScopeArgument(IMultitonRegistration registration, IReadOnlyList 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 newInstance; + return scopeArgument; } - + /// - /// Creates an instance of a given + /// Gets or creates a multiton instance of a given /// /// The given /// The registration of the given - /// The constructor arguments - /// The current resolve stack - /// A newly created instance of the given - private T CreateInstance(IRegistration registration, object[]? arguments, List resolveStack) + /// The arguments to resolve + /// An existing or newly created multiton instance of the given + /// No arguments given + /// Scope argument not given + private T CreateMultitonInstance(IMultitonRegistration registration, object?[]? arguments) { - if (registration is IWithParametersInternal { Parameters: { } } registrationWithParameters) - arguments = UpdateArgumentsWithRegistrationParameters(registrationWithParameters, arguments); + if (arguments == null || !arguments.Any()) + throw new MultitonResolveException("Can not resolve multiton without arguments.", registration.InterfaceType); + + object scopeArgument = TryGetMultitonScopeArgument(registration, arguments); - T instance; - if (registration is IOpenGenericRegistration openGenericRegistration) + //if a multiton for the given scope exists return it + 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); - - //create generic implementation type from generic arguments of T - Type genericImplementationType = openGenericRegistration.ImplementationType.MakeGenericType(typeof(T).GenericTypeArguments); + T createdInstance = Creator.CreateInstance(registration.ImplementationType, arguments[1..]); + matchingMultitons.instances.Add(scopeArgument, createdInstance); - instance = (T) Activator.CreateInstance(genericImplementationType, arguments); - } - else if (registration is ITypedRegistration defaultRegistration) - { - arguments = ResolveTypeCreationArguments(defaultRegistration.ImplementationType, arguments, resolveStack); - instance = (T) Activator.CreateInstance(defaultRegistration.ImplementationType, arguments); + return createdInstance; } - else if (registration is ISingleTypeRegistration 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 - { - 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? + T newInstance = Creator.CreateInstance(registration.ImplementationType, arguments[1..]); + _multitons.Add((registration.ImplementationType, registration.Scope, new ConditionalWeakTable { { scopeArgument, newInstance } })); - return instance; + return newInstance; } /// @@ -494,41 +583,6 @@ namespace LightweightIocContainer return arguments; } - /// - /// Resolve the missing type creation arguments - /// - /// The that will be created - /// The existing arguments - /// The current resolve stack - /// An array of all needed constructor arguments to create the - /// No matching constructor was found for the given or resolvable arguments - private object[]? ResolveTypeCreationArguments(Type type, object[]? arguments, List resolveStack) - { - (bool result, List? parameters, NoMatchingConstructorFoundException? exception) = TryGetTypeResolveStack(type, arguments, resolveStack); - - 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(); - } - - if (exception != null) - throw exception; - - return null; - } - /// /// Try to get the resolve stack for a given /// @@ -540,7 +594,7 @@ namespace LightweightIocContainer /// parameters: The parameters needed to resolve the given /// exception: A if no matching constructor was found /// - private (bool result, List? parameters, NoMatchingConstructorFoundException? exception) TryGetTypeResolveStack(Type type, object[]? arguments, List resolveStack) + private (bool result, List? parameters, NoMatchingConstructorFoundException? exception) TryGetTypeResolveStack(Type type, object[]? arguments, List resolveStack) { NoMatchingConstructorFoundException? noMatchingConstructorFoundException = null; @@ -548,7 +602,7 @@ namespace LightweightIocContainer List sortedConstructors = TryGetSortedConstructors(type); foreach (ConstructorInfo constructor in sortedConstructors) { - (bool result, List? parameters, List? exceptions) = TryGetConstructorResolveStack(constructor, arguments, resolveStack); + (bool result, List? parameters, List? exceptions) = TryGetConstructorResolveStack(constructor, arguments, resolveStack); if (result) return (true, parameters, null); @@ -572,14 +626,14 @@ namespace LightweightIocContainer /// parameters: The parameters needed to resolve the given /// exception: A List of s if the constructor is not matching /// - private (bool result, List? parameters, List? exceptions) TryGetConstructorResolveStack(ConstructorInfo constructor, object[]? arguments, List resolveStack) + private (bool result, List? parameters, List? exceptions) TryGetConstructorResolveStack(ConstructorInfo constructor, object[]? arguments, List resolveStack) { List constructorParameters = constructor.GetParameters().ToList(); if (!constructorParameters.Any()) return (true, null, null); List exceptions = new(); - List parameters = new(); + List parameters = new(); List? passedArguments = null; if (arguments != null) @@ -587,11 +641,11 @@ namespace LightweightIocContainer foreach (ParameterInfo parameter in constructorParameters) { - object fittingArgument = new InternalResolvePlaceholder(); + object? fittingArgument = new InternalResolvePlaceholder(); if (passedArguments != null) { fittingArgument = passedArguments.FirstOrGiven(a => - a?.GetType() == parameter.ParameterType || parameter.ParameterType.IsInstanceOfType(a)); + a.GetType() == parameter.ParameterType || parameter.ParameterType.IsInstanceOfType(a)); if (fittingArgument is not InternalResolvePlaceholder) passedArguments.Remove(fittingArgument); @@ -601,30 +655,7 @@ namespace LightweightIocContainer { try { - IRegistration registration = FindRegistration(parameter.ParameterType) ?? throw new TypeNotRegisteredException(parameter.ParameterType); - - List 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? parametersToResolve, NoMatchingConstructorFoundException? exception) = - TryGetTypeResolveStack(registeredType, argumentsForRegistration, internalResolveStack); - - if (result) - fittingArgument = new InternalToBeResolvedPlaceholder(registeredType, parametersToResolve); - else if (exception != null) - exceptions.Add(new ConstructorNotMatchingException(constructor, exception)); - } + fittingArgument = TryResolveNonGeneric(parameter.ParameterType, null, resolveStack); } catch (Exception exception) { @@ -654,15 +685,9 @@ namespace LightweightIocContainer /// /// The given /// The for the given - private IRegistration? FindRegistration() => FindRegistration(typeof(T)); - - /// - /// Find the for the given - /// - /// The given - /// The for the given - private IRegistration? FindRegistration(Type type) + private IRegistration? FindRegistration() { + Type type = typeof(T); IRegistration? registration = Registrations.FirstOrDefault(r => r.InterfaceType == type); if (registration != null) return registration; @@ -709,15 +734,6 @@ namespace LightweightIocContainer _ => throw new UnknownRegistrationException($"Unknown registration used: {registration.GetType().Name}.") }; - /// - /// Non generic method to get the implementation type for the given - /// - /// The given of the interface - /// The given - /// The implementation for the given - /// Unknown passed - private Type GetTypeNonGeneric(Type type, IRegistration registration) => (Type) GenericMethodCaller.CallPrivate(this, nameof(GetType), type, registration); - /// /// Check the given resolve stack for circular dependencies /// @@ -725,23 +741,14 @@ namespace LightweightIocContainer /// The given /// The new resolve stack /// A circular dependency was detected - private List CheckForCircularDependencies(List? resolveStack) => CheckForCircularDependencies(typeof(T), resolveStack); - - /// - /// Check the given resolve stack for circular dependencies - /// - /// The given - /// The given resolve stack - /// The new resolve stack - /// A circular dependency was detected - private List CheckForCircularDependencies(Type type, List? resolveStack) + private List CheckForCircularDependencies(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 + 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(type); //add currently resolving type to the stack + resolveStack.Add(typeof(T)); //add currently resolving type to the stack return resolveStack; } diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml index 67a2774..f61473c 100644 --- a/LightweightIocContainer/LightweightIocContainer.xml +++ b/LightweightIocContainer/LightweightIocContainer.xml @@ -744,6 +744,11 @@ The that is used to register an Interface and extends the with fluent options + + + A base without generic interface + + The to register either only an interface or only a @@ -1535,6 +1540,11 @@ that is invoked instead of creating an instance of this the default way + + + True if is set, false if not + + Pass a that will be invoked instead of creating an instance of this 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 - + The to be resolved - The parameters needed to resolve the + The parameters needed to resolve the diff --git a/LightweightIocContainer/ResolvePlaceholders/IInternalToBeResolvedPlaceholder.cs b/LightweightIocContainer/ResolvePlaceholders/IInternalToBeResolvedPlaceholder.cs new file mode 100644 index 0000000..1f2646c --- /dev/null +++ b/LightweightIocContainer/ResolvePlaceholders/IInternalToBeResolvedPlaceholder.cs @@ -0,0 +1,18 @@ +// Author: Gockner, Simon +// Created: 2021-12-14 +// Copyright(c) 2021 SimonG. All Rights Reserved. + +using System; + +namespace LightweightIocContainer.ResolvePlaceholders; + +/// +/// An internal placeholder that is used to hold types that need to be resolved during the resolving process +/// +internal interface IInternalToBeResolvedPlaceholder +{ + /// + /// The to be resolved + /// + Type ResolvedType { get; } +} \ No newline at end of file diff --git a/LightweightIocContainer/ResolvePlaceholders/InternalFactoryMethodPlaceholder.cs b/LightweightIocContainer/ResolvePlaceholders/InternalFactoryMethodPlaceholder.cs new file mode 100644 index 0000000..51005a7 --- /dev/null +++ b/LightweightIocContainer/ResolvePlaceholders/InternalFactoryMethodPlaceholder.cs @@ -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; + +/// +/// An internal placeholder that is used to hold factory methods for types that need to be resolved during the resolve process +/// +internal class InternalFactoryMethodPlaceholder : IInternalToBeResolvedPlaceholder +{ + public InternalFactoryMethodPlaceholder(ISingleTypeRegistration singleTypeRegistration) + { + ResolvedType = singleTypeRegistration.InterfaceType; + FactoryMethod = singleTypeRegistration.FactoryMethod ?? throw new InvalidOperationException("Factory method can't be null."); + } + + /// + /// The to be resolved + /// + public Type ResolvedType { get; } + + /// + /// The + /// + public Func FactoryMethod { get; } +} \ No newline at end of file diff --git a/LightweightIocContainer/ResolvePlaceholders/InternalToBeResolvedPlaceholder.cs b/LightweightIocContainer/ResolvePlaceholders/InternalToBeResolvedPlaceholder.cs index dd7ed6f..09842da 100644 --- a/LightweightIocContainer/ResolvePlaceholders/InternalToBeResolvedPlaceholder.cs +++ b/LightweightIocContainer/ResolvePlaceholders/InternalToBeResolvedPlaceholder.cs @@ -4,17 +4,19 @@ using System; using System.Collections.Generic; +using LightweightIocContainer.Interfaces.Registrations; namespace LightweightIocContainer.ResolvePlaceholders { /// /// An internal placeholder that is used to hold types that need to be resolved during the resolving process /// - internal class InternalToBeResolvedPlaceholder + internal class InternalToBeResolvedPlaceholder : IInternalToBeResolvedPlaceholder { - public InternalToBeResolvedPlaceholder(Type resolvedType, List? parameters) + public InternalToBeResolvedPlaceholder(Type resolvedType, IRegistration resolvedRegistration, List? parameters) { ResolvedType = resolvedType; + ResolvedRegistration = resolvedRegistration; Parameters = parameters; } @@ -24,8 +26,13 @@ namespace LightweightIocContainer.ResolvePlaceholders public Type ResolvedType { get; } /// - /// The parameters needed to resolve the + /// The to be resolved /// - public List? Parameters { get; } + public IRegistration ResolvedRegistration { get; } + + /// + /// The parameters needed to resolve the + /// + public List? Parameters { get; } } } \ No newline at end of file