A lightweight IOC Container that is powerful enough to do all the things you need it to do.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

612 lines
35 KiB

// Author: simon.gockner
// Created: 2019-05-20
// Copyright(c) 2019 SimonG. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
using LightweightIocContainer.Exceptions;
using LightweightIocContainer.Interfaces;
using LightweightIocContainer.Interfaces.Factories;
using LightweightIocContainer.Interfaces.Installers;
using LightweightIocContainer.Interfaces.Registrations;
using LightweightIocContainer.Interfaces.Registrations.Fluent;
using LightweightIocContainer.Registrations;
namespace LightweightIocContainer
{
/// <summary>
/// The main container that carries all the <see cref="IRegistration"/>s and can resolve all the types you'll ever want
/// </summary>
public class IocContainer : IIocContainer, IResolver
{
private readonly RegistrationFactory _registrationFactory;
private readonly List<(Type type, object instance)> _singletons = new();
private readonly List<(Type type, Type scope, ConditionalWeakTable<object, object> instances)> _multitons = new();
/// <summary>
/// The main container that carries all the <see cref="IRegistration"/>s and can resolve all the types you'll ever want
/// </summary>
public IocContainer()
{
_registrationFactory = new RegistrationFactory(this);
Registrations = new List<IRegistration>();
}
internal List<IRegistration> Registrations { get; }
/// <summary>
/// Install the given installers for the current <see cref="IocContainer"/>
/// </summary>
/// <param name="installers">The given <see cref="IIocInstaller"/>s</param>
/// <returns>An instance of the current <see cref="IocContainer"/></returns>
public IIocContainer Install(params IIocInstaller[] installers)
{
foreach (IIocInstaller installer in installers)
installer.Install(this);
return this;
}
/// <summary>
/// Register an Interface with a Type that implements it
/// </summary>
/// <typeparam name="TInterface">The Interface to register</typeparam>
/// <typeparam name="TImplementation">The Type that implements the interface</typeparam>
/// <param name="lifestyle">The <see cref="Lifestyle"/> for this <see cref="IRegistrationBase"/></param>
/// <returns>The created <see cref="IRegistration"/></returns>
public ITypedRegistration<TInterface, TImplementation> Register<TInterface, TImplementation>(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface
{
ITypedRegistration<TInterface, TImplementation> registration = _registrationFactory.Register<TInterface, TImplementation>(lifestyle);
Register(registration);
return registration;
}
/// <summary>
/// Register an open generic Interface with an open generic Type that implements it
/// </summary>
/// <param name="tInterface">The open generic Interface to register</param>
/// <param name="tImplementation">The open generic Type that implements the interface</param>
/// <param name="lifestyle">The <see cref="Lifestyle"/> for this <see cref="IOpenGenericRegistration"/></param>
/// <returns>The created <see cref="IRegistration"/></returns>
/// <exception cref="InvalidRegistrationException">Function can only be used to register open generic types</exception>
/// <exception cref="InvalidRegistrationException">Can't register a multiton with open generic registration</exception>
public IOpenGenericRegistration RegisterOpenGenerics(Type tInterface, Type tImplementation, Lifestyle lifestyle = Lifestyle.Transient)
{
if (!tInterface.ContainsGenericParameters)
throw new InvalidRegistrationException("This function can only be used to register open generic types.");
if (lifestyle == Lifestyle.Multiton)
throw new InvalidRegistrationException("Can't register a multiton with open generic registration."); //TODO: Is there any need for a possibility to register multitons with open generics?
IOpenGenericRegistration registration = _registrationFactory.Register(tInterface, tImplementation, lifestyle);
Register(registration);
return registration;
}
/// <summary>
/// Register multiple interfaces for a <see cref="Type"/> that implements them
/// </summary>
/// <typeparam name="TInterface1">The base interface to register</typeparam>
/// <typeparam name="TInterface2">A second interface to register</typeparam>
/// <typeparam name="TImplementation">The <see cref="Type"/> that implements both interfaces</typeparam>
/// <param name="lifestyle">The <see cref="Lifestyle"/> for this <see cref="IRegistrationBase"/></param>
/// <returns>The created <see cref="IMultipleRegistration{TInterface1,TInterface2}"/></returns>
public IMultipleRegistration<TInterface1, TInterface2, TImplementation> Register<TInterface1, TInterface2, TImplementation>(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface2, TInterface1
{
IMultipleRegistration<TInterface1, TInterface2, TImplementation> multipleRegistration = _registrationFactory.Register<TInterface1, TInterface2, TImplementation>(lifestyle);
Register(multipleRegistration);
return multipleRegistration;
}
/// <summary>
/// Register multiple interfaces for a <see cref="Type"/> that implements them
/// </summary>
/// <typeparam name="TInterface1">The base interface to register</typeparam>
/// <typeparam name="TInterface2">A second interface to register</typeparam>
/// <typeparam name="TInterface3">A third interface to register</typeparam>
/// <typeparam name="TImplementation">The <see cref="Type"/> that implements both interfaces</typeparam>
/// <param name="lifestyle">The <see cref="Lifestyle"/> for this <see cref="IRegistrationBase"/></param>
/// <returns>The created <see cref="IMultipleRegistration{TInterface1,TInterface2,TInterface3}"/></returns>
public IMultipleRegistration<TInterface1, TInterface2, TInterface3, TImplementation> Register<TInterface1, TInterface2, TInterface3, TImplementation>(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface3, TInterface2, TInterface1
{
IMultipleRegistration<TInterface1, TInterface2, TInterface3, TImplementation> multipleRegistration = _registrationFactory.Register<TInterface1, TInterface2, TInterface3, TImplementation>(lifestyle);
Register(multipleRegistration);
return multipleRegistration;
}
/// <summary>
/// Register multiple interfaces for a <see cref="Type"/> that implements them
/// </summary>
/// <typeparam name="TInterface1">The base interface to register</typeparam>
/// <typeparam name="TInterface2">A second interface to register</typeparam>
/// <typeparam name="TInterface3">A third interface to register</typeparam>
/// <typeparam name="TInterface4">A fourth interface to register</typeparam>
/// <typeparam name="TImplementation">The <see cref="Type"/> that implements both interfaces</typeparam>
/// <param name="lifestyle">The <see cref="Lifestyle"/> for this <see cref="IRegistrationBase"/></param>
/// <returns>The created <see cref="IMultipleRegistration{TInterface1,TInterface2,TInterface3,TInterface4}"/></returns>
public IMultipleRegistration<TInterface1, TInterface2, TInterface3, TInterface4, TImplementation> Register<TInterface1, TInterface2, TInterface3, TInterface4, TImplementation>(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface4, TInterface3, TInterface2, TInterface1
{
IMultipleRegistration<TInterface1, TInterface2, TInterface3, TInterface4, TImplementation> multipleRegistration = _registrationFactory.Register<TInterface1, TInterface2, TInterface3, TInterface4, TImplementation>(lifestyle);
Register(multipleRegistration);
return multipleRegistration;
}
/// <summary>
/// Register multiple interfaces for a <see cref="Type"/> that implements them
/// </summary>
/// <typeparam name="TInterface1">The base interface to register</typeparam>
/// <typeparam name="TInterface2">A second interface to register</typeparam>
/// <typeparam name="TInterface3">A third interface to register</typeparam>
/// <typeparam name="TInterface4">A fourth interface to register</typeparam>
/// <typeparam name="TInterface5">A fifth interface to register</typeparam>
/// <typeparam name="TImplementation">The <see cref="Type"/> that implements both interfaces</typeparam>
/// <param name="lifestyle">The <see cref="Lifestyle"/> for this <see cref="IRegistrationBase"/></param>
/// <returns>The created <see cref="IMultipleRegistration{TInterface1,TInterface2,TInterface3,TInterface4,TInterface5}"/></returns>
public IMultipleRegistration<TInterface1, TInterface2, TInterface3, TInterface4, TInterface5, TImplementation> Register<TInterface1, TInterface2, TInterface3, TInterface4, TInterface5, TImplementation>(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface5, TInterface4, TInterface3, TInterface2, TInterface1
{
IMultipleRegistration<TInterface1, TInterface2, TInterface3, TInterface4, TInterface5, TImplementation> multipleRegistration = _registrationFactory.Register<TInterface1, TInterface2, TInterface3, TInterface4, TInterface5, TImplementation>(lifestyle);
Register(multipleRegistration);
return multipleRegistration;
}
/// <summary>
/// Register a <see cref="Type"/> without an interface
/// </summary>
/// <typeparam name="TImplementation">The <see cref="Type"/> to register</typeparam>
/// <param name="lifestyle">The <see cref="Lifestyle"/> for this <see cref="IRegistrationBase"/></param>
/// <returns>The created <see cref="IRegistration"/></returns>
public ISingleTypeRegistration<TImplementation> Register<TImplementation>(Lifestyle lifestyle = Lifestyle.Transient)
{
ISingleTypeRegistration<TImplementation> registration = _registrationFactory.Register<TImplementation>(lifestyle);
Register(registration);
return registration;
}
/// <summary>
/// Register an Interface with a Type that implements it as a multiton
/// </summary>
/// <typeparam name="TInterface">The Interface to register</typeparam>
/// <typeparam name="TImplementation">The Type that implements the interface</typeparam>
/// <typeparam name="TScope">The Type of the multiton scope</typeparam>
/// <returns>The created <see cref="IRegistration"/></returns>
public IMultitonRegistration<TInterface, TImplementation> RegisterMultiton<TInterface, TImplementation, TScope>() where TImplementation : TInterface
{
IMultitonRegistration<TInterface, TImplementation> registration = _registrationFactory.RegisterMultiton<TInterface, TImplementation, TScope>();
Register(registration);
return registration;
}
/// <summary>
/// Register multiple interfaces for a <see cref="Type"/> that implements them as a multiton
/// </summary>
/// <typeparam name="TInterface1">The base interface to register</typeparam>
/// <typeparam name="TInterface2">A second interface to register</typeparam>
/// <typeparam name="TImplementation">The Type that implements the interface</typeparam>
/// <typeparam name="TScope">The Type of the multiton scope</typeparam>
/// <returns>The created <see cref="IRegistration"/></returns>
public IMultipleMultitonRegistration<TInterface1, TInterface2, TImplementation> RegisterMultiton<TInterface1, TInterface2, TImplementation, TScope>() where TImplementation : TInterface1, TInterface2
{
IMultipleMultitonRegistration<TInterface1, TInterface2, TImplementation> registration = _registrationFactory.RegisterMultiton<TInterface1, TInterface2, TImplementation, TScope>();
Register(registration);
return registration;
}
/// <summary>
/// Register an Interface as an abstract typed factory
/// </summary>
/// <typeparam name="TFactory">The abstract typed factory to register</typeparam>
/// <returns>The created <see cref="IRegistration"/></returns>
internal void RegisterFactory<TFactory>(ITypedFactory<TFactory> factory) => Register(_registrationFactory.RegisterFactory(factory));
/// <summary>
/// Add the <see cref="IRegistration"/> to the the <see cref="IocContainer"/>
/// </summary>
/// <param name="registration">The given <see cref="IRegistration"/></param>
/// <exception cref="MultipleRegistrationException">The <see cref="Type"/> is already registered in this <see cref="IocContainer"/></exception>
private void Register(IRegistration registration)
{
//if type is already registered
if (Registrations.Any(r => r.InterfaceType == registration.InterfaceType))
throw new MultipleRegistrationException(registration.InterfaceType);
//don't allow lifestyle.multiton without iMultitonRegistration
if (registration is ILifestyleProvider { Lifestyle: Lifestyle.Multiton } and not IMultitonRegistration)
throw new InvalidRegistrationException("Can't register a type as Lifestyle.Multiton without a scope (Registration is not of type IMultitonRegistration).");
Registrations.Add(registration);
}
/// <summary>
/// Register all <see cref="IMultipleRegistration{TInterface1,TImplementation}.Registrations"/> from an <see cref="IMultipleRegistration{TInterface1,TImplementation}"/>
/// </summary>
/// <typeparam name="TInterface1">The <see cref="Type"/> of the first registered interface</typeparam>
/// <typeparam name="TImplementation">The <see cref="Type"/> of the registered implementation</typeparam>
/// <param name="multipleRegistration">The <see cref="IMultipleRegistration{TInterface1,TImplementation}"/></param>
private void Register<TInterface1, TImplementation>(IMultipleRegistration<TInterface1, TImplementation> multipleRegistration) where TImplementation : TInterface1
{
foreach (IRegistration registration in multipleRegistration.Registrations)
Register(registration);
}
/// <summary>
/// Gets an instance of the given <see cref="Type"/>
/// </summary>
/// <typeparam name="T">The given <see cref="Type"/></typeparam>
/// <returns>An instance of the given <see cref="Type"/></returns>
public virtual T Resolve<T>() => ResolveInternal<T>(null);
/// <summary>
/// Gets an instance of the given <see cref="Type"/>
/// </summary>
/// <typeparam name="T">The given <see cref="Type"/></typeparam>
/// <param name="arguments">The constructor arguments</param>
/// <returns>An instance of the given <see cref="Type"/></returns>
public T Resolve<T>(params object[] arguments) => ResolveInternal<T>(arguments);
/// <summary>
/// Gets an instance of the given <see cref="Type"/>
/// </summary>
/// <param name="type">The given <see cref="Type"/></param>
/// <param name="arguments">The constructor arguments</param>
/// <param name="resolveStack">The current resolve stack</param>
/// <returns>An instance of the given <see cref="Type"/></returns>
/// <exception cref="InternalResolveException">Could not find function <see cref="ResolveInternal{T}"/></exception>
internal object Resolve(Type type, object[] arguments, List<Type> resolveStack) =>
GenericMethodCaller.Call(this, nameof(ResolveInternal), type, BindingFlags.NonPublic | BindingFlags.Instance, 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>
/// <exception cref="TypeNotRegisteredException">The given <see cref="Type"/> is not registered in this <see cref="IocContainer"/></exception>
/// <exception cref="UnknownRegistrationException">The registration for the given <see cref="Type"/> has an unknown <see cref="Type"/></exception>
private T ResolveInternal<T>(object[] arguments, List<Type> resolveStack = null)
{
IRegistration registration = FindRegistration<T>();
if (registration == null)
throw new TypeNotRegisteredException(typeof(T));
//Circular dependency check
if (resolveStack == null) //first resolve call
resolveStack = new List<Type> {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
T resolvedInstance = registration switch
{
RegistrationBase { Lifestyle: Lifestyle.Singleton } defaultRegistration => GetOrCreateSingletonInstance<T>(defaultRegistration, arguments, resolveStack),
RegistrationBase { Lifestyle: Lifestyle.Multiton } and IMultitonRegistration multitonRegistration => GetOrCreateMultitonInstance<T>(multitonRegistration, arguments, resolveStack),
RegistrationBase defaultRegistration => CreateInstance<T>(defaultRegistration, arguments, resolveStack),
ITypedFactoryRegistration<T> typedFactoryRegistration => typedFactoryRegistration.Factory.Factory,
_ => 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
return resolvedInstance;
}
/// <summary>
/// Gets or creates a singleton instance of a given <see cref="Type"/>
/// </summary>
/// <typeparam name="T">The given <see cref="Type"/></typeparam>
/// <param name="registration">The registration of the given <see cref="Type"/></param>
/// <param name="arguments">The arguments to resolve</param>
/// <param name="resolveStack">The current resolve stack</param>
/// <returns>An existing or newly created singleton instance of the given <see cref="Type"/></returns>
private T GetOrCreateSingletonInstance<T>(IRegistration registration, object[] arguments, List<Type> resolveStack)
{
Type type = registration switch
{
ITypedRegistration typedRegistration => typedRegistration.ImplementationType,
ISingleTypeRegistration<T> 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;
if (instance != null)
return (T) instance;
//if it doesn't already exist create a new instance and add it to the list
T newInstance = CreateInstance<T>(registration, arguments, resolveStack);
_singletons.Add((type, newInstance));
return newInstance;
}
/// <summary>
/// Gets or creates a multiton instance of a given <see cref="Type"/>
/// </summary>
/// <typeparam name="T">The given <see cref="Type"/></typeparam>
/// <param name="registration">The registration of the given <see cref="Type"/></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>
/// <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())
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));
//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))
return (T) instance;
T createdInstance = CreateInstance<T>(registration, arguments, resolveStack);
instances.Add(scopeArgument, createdInstance);
return createdInstance;
}
T newInstance = CreateInstance<T>(registration, arguments, resolveStack);
ConditionalWeakTable<object, object> weakTable = new();
weakTable.Add(scopeArgument, newInstance);
_multitons.Add((registration.ImplementationType, registration.Scope, weakTable));
return newInstance;
}
/// <summary>
/// Creates an instance of a given <see cref="Type"/>
/// </summary>
/// <typeparam name="T">The given <see cref="Type"/></typeparam>
/// <param name="registration">The registration of the given <see cref="Type"/></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>
private T CreateInstance<T>(IRegistration registration, object[] arguments, List<Type> resolveStack)
{
if (registration is IWithParametersInternal { Parameters: { } } registrationWithParameters)
arguments = UpdateArgumentsWithRegistrationParameters(registrationWithParameters, arguments);
T instance;
if (registration is IOpenGenericRegistration openGenericRegistration)
{
arguments = ResolveConstructorArguments(openGenericRegistration.ImplementationType, arguments, resolveStack);
//create generic implementation type from generic arguments of T
Type genericImplementationType = openGenericRegistration.ImplementationType.MakeGenericType(typeof(T).GenericTypeArguments);
instance = (T) Activator.CreateInstance(genericImplementationType, arguments);
}
else if (registration is ITypedRegistration defaultRegistration)
{
arguments = ResolveConstructorArguments(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
{
arguments = ResolveConstructorArguments(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;
}
/// <summary>
/// Update the given arguments with the <see cref="IWithParametersInternal.Parameters"/> of the given <see cref="IRegistrationBase"/>
/// </summary>
/// <param name="registration">The <see cref="IRegistrationBase"/> of the given <see cref="Type"/></param>
/// <param name="arguments">The constructor arguments</param>
/// <returns>The argument list updated with the <see cref="IWithParametersInternal.Parameters"/></returns>
private object[] UpdateArgumentsWithRegistrationParameters(IWithParametersInternal registration, object[] arguments)
{
if (arguments != null && arguments.Any()) //if more arguments were passed to resolve
{
int argumentsSize = registration.Parameters.Length + arguments.Length;
object[] newArguments = new object[argumentsSize];
for (int i = 0; i < argumentsSize; i++)
{
if (i < registration.Parameters.Length) //if `i` is bigger than the length of the parameters, take the given arguments
{
object currentParameter = registration.Parameters[i];
if (currentParameter is not InternalResolvePlaceholder) //use the parameter at the current index if it is not a placeholder
{
newArguments[i] = currentParameter;
continue;
}
}
object firstArgument = arguments.FirstOrGiven<object, InternalResolvePlaceholder>(a => a is not InternalResolvePlaceholder); //find the first argument that is not a placeholder
if (firstArgument is InternalResolvePlaceholder) //no more arguments available
break; //there won't be any more arguments
newArguments[i] = firstArgument;
int indexOfFirstArgument = Array.IndexOf(arguments, firstArgument);
arguments[indexOfFirstArgument] = new InternalResolvePlaceholder();
}
arguments = newArguments;
}
else //no more arguments were passed to resolve -> only use parameters set during registration
arguments = registration.Parameters;
return arguments;
}
/// <summary>
/// Resolve the missing constructor 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>
[CanBeNull]
private object[] ResolveConstructorArguments(Type type, object[] arguments, List<Type> resolveStack)
{
//find best ctor
List<ConstructorInfo> sortedConstructors = type.GetConstructors().OrderByDescending(c => c.GetParameters().Length).ToList();
if (!sortedConstructors.Any()) //no public constructor available
throw new NoPublicConstructorFoundException(type);
NoMatchingConstructorFoundException noMatchingConstructorFoundException = null;
foreach (ConstructorInfo ctor in sortedConstructors)
{
try
{
List<object> argumentsList = arguments?.ToList();
List<object> ctorParams = new();
ParameterInfo[] parameters = ctor.GetParameters();
foreach (ParameterInfo parameter in parameters)
{
object fittingArgument = new InternalResolvePlaceholder();
if (argumentsList != null)
{
fittingArgument = argumentsList.FirstOrGiven<object, InternalResolvePlaceholder>(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
{
try
{
fittingArgument = Resolve(parameter.ParameterType, null, resolveStack);
}
catch (Exception)
{
fittingArgument = argumentsList.FirstOrGiven<object, InternalResolvePlaceholder>(a => parameter.ParameterType.GetDefault() == a);
if (fittingArgument is not InternalResolvePlaceholder)
{
int index = argumentsList.IndexOf(fittingArgument);
argumentsList[index] = new InternalResolvePlaceholder();
}
}
}
}
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);
}
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 (noMatchingConstructorFoundException != null)
throw noMatchingConstructorFoundException;
return null;
}
[CanBeNull]
private IRegistration FindRegistration<T>()
{
IRegistration registration = Registrations.FirstOrDefault(r => r.InterfaceType == typeof(T));
if (registration != null)
return registration;
registration = Registrations.OfType<ITypedRegistration>().FirstOrDefault(r => r.ImplementationType == typeof(T));
if (registration != null)
return registration;
//check for open generic registration
if (!typeof(T).GenericTypeArguments.Any())
return null;
List<IRegistration> openGenericRegistrations = Registrations.Where(r => r.InterfaceType.ContainsGenericParameters).ToList();
return !openGenericRegistrations.Any() ? null : openGenericRegistrations.FirstOrDefault(r => r.InterfaceType == typeof(T).GetGenericTypeDefinition());
}
/// <summary>
/// Clear the multiton instances of the given <see cref="Type"/> from the registered multitons list
/// </summary>
/// <typeparam name="T">The <see cref="Type"/> to clear the multiton instances</typeparam>
public void ClearMultitonInstances<T>()
{
IRegistration registration = FindRegistration<T>();
if (!(registration is IMultitonRegistration multitonRegistration))
return;
var multitonInstance = _multitons.FirstOrDefault(m => m.type == multitonRegistration.ImplementationType);
//it is allowed to clear a non existing multiton instance (don't throw an exception)
if (multitonInstance == default)
return;
_multitons.Remove(multitonInstance);
}
/// <summary>
/// Is the given <see cref="Type"/> registered with this <see cref="IocContainer"/>
/// </summary>
/// <typeparam name="T">The given <see cref="Type"/></typeparam>
/// <returns>True if the given <see cref="Type"/> is registered with this <see cref="IocContainer"/>, false if not</returns>
public bool IsTypeRegistered<T>() => FindRegistration<T>() != null;
/// <summary>
/// The <see cref="IDisposable.Dispose"/> method
/// </summary>
public void Dispose()
{
Registrations.Clear();
_singletons.Clear();
_multitons.Clear();
}
}
}