From 55285cf085a5b6e4fe2371b987dce21a44084814 Mon Sep 17 00:00:00 2001 From: Simon Gockner Date: Tue, 4 Jun 2019 22:32:34 +0200 Subject: [PATCH] - add InjectorContainer --- LightweightIocContainer/InjectorContainer.cs | 220 ++++++++++++++++++ .../Interfaces/IInjectorContainer.cs | 51 ++++ 2 files changed, 271 insertions(+) create mode 100644 LightweightIocContainer/InjectorContainer.cs create mode 100644 LightweightIocContainer/Interfaces/IInjectorContainer.cs diff --git a/LightweightIocContainer/InjectorContainer.cs b/LightweightIocContainer/InjectorContainer.cs new file mode 100644 index 0000000..33b18a7 --- /dev/null +++ b/LightweightIocContainer/InjectorContainer.cs @@ -0,0 +1,220 @@ +// 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 LightweightIocContainer.Exceptions; +using LightweightIocContainer.Interfaces; +using LightweightIocContainer.Interfaces.Registrations; + +namespace LightweightIocContainer +{ + /// + /// The main container that carries all the s and can resolve all the types you'll ever want + /// + public class InjectorContainer : IInjectorContainer + { + private readonly List _registrations = new List(); + private readonly List<(Type type, object instance)> _singletons = new List<(Type, object)>(); //TODO: Think about the usage of ConditionalWeakTable<> + + /// + /// Install the given installers for the current + /// + /// The given s + /// An instance of the current + public IInjectorContainer Install(params IInjectorInstaller[] installers) + { + foreach (var installer in installers) + { + installer.Install(this); + } + + return this; + } + + /// + /// Add the to the the + /// + /// The given + public void Register(IRegistrationBase registration) + { + _registrations.Add(registration); + } + + /// + /// Gets an instance of the given type + /// + /// The given type + /// An instance of the given type + public T Resolve() + { + return ResolveInternal(null); + } + + /// + /// Gets an instance of the given type + /// + /// The given type + /// The constructor arguments + /// An instance of the given type + public T Resolve(params object[] arguments) + { + return ResolveInternal(arguments); + } + + /// + /// Gets an instance of the given type + /// + /// The given type + /// The constructor arguments + /// An instance of the given type + /// Could not find function + public object Resolve(object type, object arguments) //somehow the order of the arguments is different in the application compared to the unit test + { + Type realType; + object[] realArguments; + + if (type == null || type.GetType().IsArray) + { + realType = (Type) arguments; + realArguments = (object[]) type; + } + else + { + realType = (Type) type; + realArguments = (object[]) arguments; + } + + var resolveMethod = typeof(InjectorContainer).GetMethod(nameof(ResolveInternal), BindingFlags.NonPublic | BindingFlags.Instance); + var genericResolveMethod = resolveMethod?.MakeGenericMethod(realType); + + if (genericResolveMethod == null) + throw new InternalResolveException($"Could not find function {nameof(ResolveInternal)}"); + + return genericResolveMethod.Invoke(this, new object[] {realArguments}); + } + + /// + /// Gets an instance of a given registered type + /// + /// The registered type + /// The constructor arguments + /// An instance of the given registered type + /// The given type is not registered in this + /// The registration for the given type has an unknown type + private T ResolveInternal(params object[] arguments) + { + IRegistrationBase registration = _registrations.FirstOrDefault(r => r.InterfaceType == typeof(T)); + if (registration == null) + throw new TypeNotRegisteredException(typeof(T)); + + if (registration is IDefaultRegistration defaultRegistration) + { + if (defaultRegistration.Lifestyle == Lifestyle.Singleton) + return GetOrCreateSingletonInstance(defaultRegistration, arguments); + + return CreateInstance(defaultRegistration, arguments); + } + else if (registration is ITypedFactoryRegistration typedFactoryRegistration) + { + return typedFactoryRegistration.Factory.Factory; + } + else + throw new UnknownRegistrationException($"There is no registration of type {registration.GetType().Name}."); + } + + /// + /// Gets or creates a singleton instance of a given type + /// + /// The given type + /// The registration of the given type + /// The constructor arguments + /// An existing or newly created singleton instance of the given type + private T GetOrCreateSingletonInstance(IDefaultRegistration registration, params object[] arguments) + { + //if a singleton instance exists return it + object instance = _singletons.FirstOrDefault(s => s.type == typeof(T)).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(registration, arguments); + _singletons.Add((typeof(T), newInstance)); + + return newInstance; + } + + /// + /// Creates an instance of a given type + /// + /// The given type + /// The registration of the given type + /// The constructor arguments + /// A newly created instance of the given type + private T CreateInstance(IDefaultRegistration registration, params object[] arguments) + { + arguments = ResolveConstructorArguments(registration.ImplementationType, arguments); + T instance = (T) Activator.CreateInstance(registration.ImplementationType, arguments); + registration.OnCreateAction?.Invoke(instance); //TODO: Allow async OnCreateAction? + + return instance; + } + + /// + /// Resolve the missing constructor arguments + /// + /// The type that will be created + /// The existing arguments + /// An array of all needed constructor arguments to create + private object[] ResolveConstructorArguments(Type type, object[] arguments) + { + //find best ctor + var sortedCtors = type.GetConstructors().OrderByDescending(c => c.GetParameters().Length); + foreach (var ctor in sortedCtors) + { + try + { + List argumentsList = arguments?.ToList(); + List ctorParams = new List(); + + var parameters = ctor.GetParameters(); + foreach (var parameter in parameters) + { + object fittingArgument = null; + if (argumentsList != null) + { + fittingArgument = argumentsList.FirstOrDefault(a => a?.GetType() == parameter.ParameterType); + if (fittingArgument != null) + { + int index = argumentsList.IndexOf(fittingArgument); + argumentsList[index] = null; + } + } + + if (fittingArgument == null) + ctorParams.Add(Resolve(parameter.ParameterType, null)); + else + ctorParams.Add(fittingArgument); + } + + return ctorParams.ToArray(); + } + catch (Exception ex) //TODO: Decide what exactly to do in this case + { + continue; + } + } + + return null; + } + + public void Dispose() + { + _registrations.Clear(); + _singletons.Clear(); + } + } +} \ No newline at end of file diff --git a/LightweightIocContainer/Interfaces/IInjectorContainer.cs b/LightweightIocContainer/Interfaces/IInjectorContainer.cs new file mode 100644 index 0000000..888dd72 --- /dev/null +++ b/LightweightIocContainer/Interfaces/IInjectorContainer.cs @@ -0,0 +1,51 @@ +// Author: simon.gockner +// Created: 2019-05-20 +// Copyright(c) 2019 SimonG. All Rights Reserved. + +using System; +using LightweightIocContainer.Interfaces.Registrations; + +namespace LightweightIocContainer.Interfaces +{ + /// + /// The main container that carries all the s and can resolve all the types you'll ever want + /// + public interface IInjectorContainer : IDisposable + { + /// + /// Install the given installers for the current + /// + /// The given s + /// An instance of the current + IInjectorContainer Install(params IInjectorInstaller[] installers); + + /// + /// Add the to the the + /// + /// The given + void Register(IRegistrationBase registration); + + /// + /// Gets an instance of the given type + /// + /// The given type + /// An instance of the given type + T Resolve(); + + /// + /// Gets an instance of the given type + /// + /// The given type + /// The constructor arguments + /// An instance of the given type + T Resolve(params object[] arguments); + + /// + /// Gets an instance of the given type + /// + /// The given type + /// The constructor arguments + /// An instance of the given type + object Resolve(object type, object arguments); + } +} \ No newline at end of file