// Author: simon.gockner
// Created: 2019-05-20
// Copyright(c) 2019 SimonG. All Rights Reserved.
using System.Reflection;
using System.Runtime.CompilerServices;
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;
using LightweightIocContainer.ResolvePlaceholders;
namespace LightweightIocContainer;
///
/// The main container that carries all the s and can resolve all the types you'll ever want
///
public class IocContainer : IIocContainer, IIocResolver
{
private readonly RegistrationFactory _registrationFactory;
private readonly List<(Type type, object? instance)> _singletons = [];
private readonly List<(Type type, Type scope, ConditionalWeakTable instances)> _multitons = [];
private readonly List _ignoreConstructorAttributes;
///
/// The main container that carries all the s and can resolve all the types you'll ever want
///
public IocContainer()
{
_registrationFactory = new RegistrationFactory(this);
_ignoreConstructorAttributes = [];
Registrations = [];
}
internal List Registrations { get; }
///
/// Install the given installers for the current
///
/// The given s
/// An instance of the current
public IIocContainer Install(params IIocInstaller[] installers)
{
foreach (IIocInstaller installer in installers)
{
RegistrationCollector registrationCollector = new(_registrationFactory);
installer.Install(registrationCollector);
registrationCollector.Registrations.ForEach(Register);
}
return this;
}
///
/// Register an at this
///
/// The that creates an
public void Register(Func addRegistration)
{
RegistrationCollector registrationCollector = new(_registrationFactory);
addRegistration(registrationCollector);
registrationCollector.Registrations.ForEach(Register);
}
///
/// Register an Interface as an abstract typed factory
///
/// The abstract typed factory to register
/// The created
internal void RegisterFactory(ITypedFactory factory)
{
ITypedFactoryRegistration typedFactoryRegistration = _registrationFactory.RegisterFactory(factory);
if (!Registrations.Contains(typedFactoryRegistration))
Registrations.Add(typedFactoryRegistration);
}
///
/// Add the to the the
///
/// The given
/// The is already registered in this
private void Register(IRegistration registration)
{
IRegistration? sameTypeRegistration = Registrations.FirstOrDefault(r => r.InterfaceType == registration.InterfaceType);
if (sameTypeRegistration != null && !registration.Equals(sameTypeRegistration)) //if type is already registered differently
throw new MultipleRegistrationException(registration.InterfaceType);
if (registration is IInternalValidationProvider validationProvider)
validationProvider.Validate();
Registrations.Add(registration);
}
///
/// Gets an instance of the given
///
/// The given
/// An instance of the given
public virtual T Resolve() => ResolveInternal(null);
///
/// Gets an instance of the given
///
/// The given
/// The constructor arguments
/// An instance of the given
public T Resolve(params object[] arguments) => ResolveInternal(arguments);
///
/// Gets an instance of the given for a factory
///
/// The given
/// The constructor arguments
/// An instance of the given
public T FactoryResolve(params object?[] arguments) => ResolveInternal(arguments, null, true);
///
/// Gets an instance of a given registered
///
/// The registered
/// The constructor arguments
/// The current resolve stack
/// True if resolve is called from factory, false (default) if not
/// An instance of the given registered
private T ResolveInternal(object?[]? arguments, List? resolveStack = null, bool isFactoryResolve = false)
{
(bool success, object resolvedObject, Exception? exception) = TryResolve(arguments, resolveStack, isFactoryResolve);
if (success)
return ResolveInstance(resolvedObject);
if (exception is not null)
throw exception;
throw new Exception("Resolve Error");
}
///
/// Tries to resolve the given with the given arguments
///
/// The given arguments
/// The current resolve stack
/// True if resolve is called from factory, false (default) if not
/// 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
/// A direct resolve with a registered factory is not allowed
/// 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 (bool success, object resolvedObject, Exception? exception) TryResolve(object?[]? arguments, List? resolveStack, bool isFactoryResolve = false)
{
IRegistration? registration = FindRegistration();
if (registration == null)
return (false, new object(), new TypeNotRegisteredException(typeof(T)));
List internalResolveStack = resolveStack == null ? [] : [..resolveStack];
(bool success, internalResolveStack, CircularDependencyException? circularDependencyException) = CheckForCircularDependencies(internalResolveStack);
if (!success && circularDependencyException is not null)
return (false, new object(), circularDependencyException);
if (!success)
throw new Exception("Invalid return type!");
object? existingInstance = TryGetExistingInstance(registration, arguments);
if (existingInstance != null)
return (true, existingInstance, null);
switch (registration)
{
case IWithFactoryInternal { Factory: not null } when !isFactoryResolve:
return (false, new object(), new DirectResolveWithRegisteredFactoryNotAllowed(typeof(T)));
case ISingleTypeRegistration { InterfaceType.IsInterface: true, FactoryMethod: null } singleTypeRegistration:
return (false, new object(), new InvalidRegistrationException($"Can't register an interface without its implementation type or without a factory method (Type: {singleTypeRegistration.InterfaceType})."));
case ISingleTypeRegistration { FactoryMethod: not null } singleTypeRegistration:
return (true, new InternalFactoryMethodPlaceholder(singleTypeRegistration), null);
}
if (registration is IWithParametersInternal { Parameters: not null } 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())
return (false, new object(), new MultitonResolveException("Can not resolve multiton without arguments.", registration.InterfaceType));
object multitonScopeArgument = TryGetMultitonScopeArgument(multitonRegistration, arguments);
parametersToResolve ??= [];
parametersToResolve.Insert(0, multitonScopeArgument); //insert scope at first place, won't be passed to ctor when creating multiton
}
if (result)
return (true, new InternalToBeResolvedPlaceholder(registeredType, registration, parametersToResolve), null);
if (exception != null)
return (false, new object(), exception);
return (false, new object(), 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
/// True if resolve is called from factory, false (default) if not
/// 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
/// A direct resolve with a registered factory is not allowed
/// 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
internal (bool success, object resolvedObject, Exception? exception) TryResolveNonGeneric(Type type, object?[]? arguments, List? resolveStack, bool isFactoryResolve = false)
{
MethodInfo? method = typeof(IocContainer).GetMethod(nameof(TryResolve), BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo? genericMethod = method?.MakeGenericMethod(type);
if (genericMethod == null)
throw new GenericMethodNotFoundException(nameof(TryResolve));
object? resolvedValue = genericMethod.Invoke(this, [arguments, resolveStack, isFactoryResolve]);
if (resolvedValue is not ValueTuple resolvedTuple)
throw new Exception("Invalid return value!");
return resolvedTuple;
}
///
/// Recursively resolve a with the given parameters for an
///
/// The that includes the type and resolve stack
/// A recursively resolved instance of the given
private T ResolvePlaceholder(InternalToBeResolvedPlaceholder toBeResolvedPlaceholder)
{
object? existingInstance = TryGetExistingInstance(toBeResolvedPlaceholder.ResolvedRegistration, toBeResolvedPlaceholder.Parameters);
if (existingInstance is T instance)
return instance;
if (toBeResolvedPlaceholder.Parameters == null)
return CreateInstance(toBeResolvedPlaceholder.ResolvedRegistration, null);
List parameters = [];
foreach (object? parameter in toBeResolvedPlaceholder.Parameters)
{
if (parameter != null)
{
Type type = parameter is IInternalToBeResolvedPlaceholder internalToBeResolvedPlaceholder ?
internalToBeResolvedPlaceholder.ResolvedType : parameter.GetType();
parameters.Add(ResolveInstanceNonGeneric(type, parameter));
}
else
parameters.Add(parameter);
}
return CreateInstance(toBeResolvedPlaceholder.ResolvedRegistration, parameters.ToArray());
}
///
/// Resolve the given object instance
///
/// 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
{
T instance => instance,
InternalToBeResolvedPlaceholder toBeResolvedPlaceholder => ResolvePlaceholder(toBeResolvedPlaceholder),
InternalFactoryMethodPlaceholder factoryMethodPlaceholder => factoryMethodPlaceholder.FactoryMethod(this),
_ => throw new InternalResolveException("Resolve returned wrong type.")
};
///
/// Resolve the given object instance without generic arguments
///
/// The of the returned instance
/// The given resolved object
/// An instance of the given resolved object
/// Resolve returned wrong type
private object? ResolveInstanceNonGeneric(Type type, object resolvedObject) =>
GenericMethodCaller.CallPrivate(this, nameof(ResolveInstance), type, resolvedObject);
///
/// Creates an instance of a given
///
/// The given
/// The registration of the given
/// The constructor arguments
/// A newly created instance of the given
private T CreateInstance(IRegistration registration, object?[]? arguments)
{
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 (registration is ILifestyleProvider { Lifestyle: Lifestyle.Singleton })
_singletons.Add((GetType(registration), instance));
if (registration is IOnCreate onCreateRegistration)
onCreateRegistration.OnCreateAction?.Invoke(instance); //TODO: Allow async OnCreateAction?
return instance;
}
///
/// Try to get an already existing instance (factory, singleton, multiton)
///
/// 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
///
/// Try to get an existing multiton instance for a given
///
/// 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.", registration.InterfaceType);
object scopeArgument = TryGetMultitonScopeArgument(registration, arguments);
//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)
return null;
return matchingMultitons.instances.TryGetValue(scopeArgument, out object? instance) && instance != null ? instance : null;
}
///
/// 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);
return scopeArgument;
}
///
/// Gets or creates a multiton instance of a given
///
/// The given
/// The registration of the given
/// 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 (arguments == null || !arguments.Any())
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 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)
{
T createdInstance = Creator.CreateInstance(registration.ImplementationType, arguments[1..]);
matchingMultitons.instances.Add(scopeArgument, createdInstance);
return createdInstance;
}
T newInstance = Creator.CreateInstance(registration.ImplementationType, arguments[1..]);
_multitons.Add((registration.ImplementationType, registration.Scope, new ConditionalWeakTable { { scopeArgument, newInstance } }));
return newInstance;
}
///
/// Update the given arguments with the of the given
///
/// The of the given
/// The constructor arguments
/// The argument list updated with the
private object?[]? UpdateArgumentsWithRegistrationParameters(IWithParametersInternal registration, object?[]? arguments)
{
if (registration.Parameters == null)
return arguments;
if (arguments != null && arguments.Any()) //if more arguments were passed to resolve
{
int argumentsSize = registration.Parameters.Count(p => p is not InternalResolvePlaceholder) + 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(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;
}
///
/// Try to get the resolve stack for a given
///
/// The given
/// The given arguments
/// The current resolve stack
///
/// result: True if successful, false if not
/// 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, IReadOnlyCollection? arguments, List resolveStack)
{
NoMatchingConstructorFoundException? noMatchingConstructorFoundException = null;
//find best ctor
List sortedConstructors = TryGetSortedConstructors(type);
foreach (ConstructorInfo constructor in sortedConstructors)
{
(bool result, List? parameters, List? exceptions) = TryGetConstructorResolveStack(type, constructor, arguments, resolveStack);
if (result)
return (true, parameters, null);
noMatchingConstructorFoundException ??= new NoMatchingConstructorFoundException(type);
exceptions?.ForEach(e => noMatchingConstructorFoundException.AddInnerException(e));
}
return (false, null, noMatchingConstructorFoundException);
}
///
/// Try to get the resolve stack for a given constructor
///
/// The that is currently getting resolved
/// The for the given constructor
/// The given arguments
/// The current resolve stack
///
/// result: True if successful, false if not
/// 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(Type type, ConstructorInfo constructor, IReadOnlyCollection? arguments, List resolveStack)
{
List constructorParameters = constructor.GetParameters().ToList();
List exceptions = [];
List parameters = [];
List? passedArguments = null;
if (arguments != null)
passedArguments = [..arguments];
foreach (ParameterInfo parameter in constructorParameters)
{
object? fittingArgument = new InternalResolvePlaceholder();
if (passedArguments != null)
{
if (parameter.ParameterType.IsGenericParameter)
{
Type? genericArgument = type.GetGenericArguments().FirstOrDefault(a => a.Name.Equals(parameter.ParameterType.Name));
if (genericArgument is not null)
{
Type genericArgumentType = typeof(T).GetGenericArguments()[genericArgument.GenericParameterPosition];
fittingArgument = passedArguments.FirstOrGiven(a =>
a?.GetType() == genericArgumentType ||
genericArgumentType.IsInstanceOfType(a) ||
a is NullParameter nullParameter && genericArgumentType.IsAssignableFrom(nullParameter.ParameterType));
}
}
else
{
fittingArgument = passedArguments.FirstOrGiven(a =>
a?.GetType() == parameter.ParameterType ||
parameter.ParameterType.IsInstanceOfType(a) ||
a is NullParameter nullParameter && parameter.ParameterType.IsAssignableFrom(nullParameter.ParameterType));
}
if (fittingArgument is not InternalResolvePlaceholder)
passedArguments.Remove(fittingArgument);
if (fittingArgument is NullParameter)
fittingArgument = null;
}
if (fittingArgument is InternalResolvePlaceholder)
{
(bool success, object? resolvedObject, Exception? exception) = TryResolveNonGeneric(parameter.ParameterType, null, resolveStack);
if (success)
fittingArgument = resolvedObject;
else if (!success && exception is not null)
exceptions.Add(new ConstructorNotMatchingException(constructor, exception));
else
throw new Exception("Invalid return value!");
if (fittingArgument is InternalResolvePlaceholder && passedArguments != null)
{
fittingArgument = passedArguments.FirstOrGiven(a => parameter.ParameterType.GetDefault() == a);
if (fittingArgument is not InternalResolvePlaceholder)
passedArguments.Remove(fittingArgument);
}
}
if (fittingArgument is InternalResolvePlaceholder && parameter.HasDefaultValue)
parameters.Add(parameter.DefaultValue);
else
parameters.Add(fittingArgument);
}
if (passedArguments == null || !passedArguments.Any())
return (!parameters.Any(p => p is InternalResolvePlaceholder), parameters, exceptions);
exceptions.Add(new ConstructorNotMatchingException(constructor, new Exception("Not all given arguments were used!")));
return (false, parameters, exceptions);
}
///
/// Find the for the given
///
/// 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)
{
IRegistration? registration = Registrations.FirstOrDefault(r => r.InterfaceType == type);
if (registration != null)
return registration;
registration = Registrations.OfType().FirstOrDefault(r => r.ImplementationType == type);
if (registration != null)
return registration;
//check for open generic registration
if (!type.GenericTypeArguments.Any())
return null;
List openGenericRegistrations = Registrations.Where(r => r.InterfaceType.ContainsGenericParameters).ToList();
return !openGenericRegistrations.Any() ? null : openGenericRegistrations.FirstOrDefault(r => r.InterfaceType == type.GetGenericTypeDefinition());
}
///
/// Try to get the sorted constructors for the given
///
/// The given
/// A list of sorted for the given
/// No public constructor was found for the given
private List TryGetSortedConstructors(Type type)
{
List sortedConstructors = type.GetConstructors()
.Where(c => !c.GetCustomAttributes().Any(a => _ignoreConstructorAttributes.Contains(a.GetType())))
.OrderByDescending(c => c.GetParameters().Length)
.ToList();
if (!sortedConstructors.Any()) //no public constructor available
throw new NoPublicConstructorFoundException(type);
return sortedConstructors;
}
///
/// Get the implementation type for the given
///
/// The given
/// The given of the interface
/// The implementation for the given
/// Unknown passed
private Type GetType(IRegistration registration) =>
registration switch
{
ITypedRegistration typedRegistration => typedRegistration.ImplementationType,
ISingleTypeRegistration singleTypeRegistration => singleTypeRegistration.InterfaceType,
_ => throw new UnknownRegistrationException($"Unknown registration used: {registration.GetType().Name}.")
};
///
/// Check the given resolve stack for circular dependencies
///
/// The given resolve stack
/// The given
/// The new resolve stack
/// A circular dependency was detected
private (bool success, List resolveStack, CircularDependencyException? exception) CheckForCircularDependencies(List? resolveStack)
{
if (resolveStack == null) //first resolve call
resolveStack = [typeof(T)]; //create new stack and add the currently resolving type to the stack
else if (resolveStack.Contains(typeof(T)))
return (false, [], 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
return (true, resolveStack, null);
}
///
/// Clear the multiton instances of the given from the registered multitons list
///
/// The to clear the multiton instances
public void ClearMultitonInstances()
{
IRegistration? registration = FindRegistration();
if (registration is not 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);
}
///
/// Is the given registered with this
///
/// The given
/// True if the given is registered with this , false if not
public bool IsTypeRegistered() => FindRegistration() != null;
///
/// Register a custom that can annotate a constructor to be ignored
///
/// The custom
/// The passed can't be used on a constructor
public void RegisterIgnoreConstructorAttribute() where T : Attribute
{
AttributeUsageAttribute? attributeUsage = typeof(T).GetCustomAttribute();
if (attributeUsage == null || !attributeUsage.ValidOn.HasFlag(AttributeTargets.Constructor))
throw new InvalidIgnoreConstructorAttributeException();
_ignoreConstructorAttributes.Add(typeof(T));
}
///
/// The method
///
public void Dispose()
{
_singletons.Where(s => FindRegistration(s.type) is IWithDisposeStrategyInternal {DisposeStrategy: DisposeStrategy.Container})
.Select(s => s.instance)
.OfType()
.ForEach(d => d.Dispose());
_multitons.Where(m => FindRegistration(m.type) is IWithDisposeStrategyInternal {DisposeStrategy: DisposeStrategy.Container})
.SelectMany(m => m.instances)
.Select(i => i.Value)
.OfType()
.ForEach(d => d.Dispose());
Registrations.Clear();
_singletons.Clear();
_multitons.Clear();
}
}