From af65b7fd46b36ea2fe17adb6230748a8afb926e4 Mon Sep 17 00:00:00 2001 From: Simon G Date: Fri, 23 Oct 2020 14:54:13 +0200 Subject: [PATCH] #12: add option to register and resolve open generic types --- .../Interfaces/IIocContainer.cs | 2 +- LightweightIocContainer/IocContainer.cs | 64 +++++++++++++++---- .../LightweightIocContainer.xml | 4 +- 3 files changed, 54 insertions(+), 16 deletions(-) diff --git a/LightweightIocContainer/Interfaces/IIocContainer.cs b/LightweightIocContainer/Interfaces/IIocContainer.cs index fe0f94a..7718672 100644 --- a/LightweightIocContainer/Interfaces/IIocContainer.cs +++ b/LightweightIocContainer/Interfaces/IIocContainer.cs @@ -29,7 +29,7 @@ namespace LightweightIocContainer.Interfaces /// The created IDefaultRegistration Register(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface; - IOpenGenericRegistration Register(Type tInterface, Type tImplementation, + IOpenGenericRegistration RegisterOpenGenerics(Type tInterface, Type tImplementation, Lifestyle lifestyle = Lifestyle.Transient); /// diff --git a/LightweightIocContainer/IocContainer.cs b/LightweightIocContainer/IocContainer.cs index bf5f1a2..430d603 100644 --- a/LightweightIocContainer/IocContainer.cs +++ b/LightweightIocContainer/IocContainer.cs @@ -68,8 +68,14 @@ namespace LightweightIocContainer return registration; } - public IOpenGenericRegistration Register(Type tInterface, Type tImplementation, Lifestyle lifestyle = Lifestyle.Transient) + 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); @@ -268,7 +274,7 @@ namespace LightweightIocContainer /// The registration for the given has an unknown private T ResolveInternal(object[] arguments, List resolveStack = null) { - IRegistration registration = _registrations.FirstOrDefault(r => r.InterfaceType == typeof(T)); + IRegistration registration = FindRegistration(); if (registration == null) throw new TypeNotRegisteredException(typeof(T)); @@ -285,11 +291,11 @@ namespace LightweightIocContainer if (registration is IRegistrationBase defaultRegistration) { if (defaultRegistration.Lifestyle == Lifestyle.Singleton) - resolvedInstance = GetOrCreateSingletonInstance(defaultRegistration, arguments, resolveStack); + resolvedInstance = GetOrCreateSingletonInstance(defaultRegistration, arguments, resolveStack); else if (defaultRegistration is IMultitonRegistration multitonRegistration && defaultRegistration.Lifestyle == Lifestyle.Multiton) resolvedInstance = GetOrCreateMultitonInstance(multitonRegistration, arguments, resolveStack); else - resolvedInstance = CreateInstance(defaultRegistration, arguments, resolveStack); + resolvedInstance = CreateInstance(defaultRegistration, arguments, resolveStack); } else if (registration is ITypedFactoryRegistration typedFactoryRegistration) { @@ -297,7 +303,10 @@ namespace LightweightIocContainer } else if (registration is IOpenGenericRegistration openGenericRegistration) { - + if (openGenericRegistration.Lifestyle == Lifestyle.Singleton) + resolvedInstance = GetOrCreateSingletonInstance(openGenericRegistration, arguments, resolveStack); + else + resolvedInstance = CreateInstance(openGenericRegistration, arguments, resolveStack); } else throw new UnknownRegistrationException($"There is no registration of type {registration.GetType().Name}."); @@ -315,13 +324,15 @@ namespace LightweightIocContainer /// The arguments to resolve /// The current resolve stack /// An existing or newly created singleton instance of the given - private T GetOrCreateSingletonInstance(IRegistrationBase registration, object[] arguments, List resolveStack) + private T GetOrCreateSingletonInstance(IRegistration registration, object[] arguments, List resolveStack) { Type type; if (registration is ITypedRegistrationBase typedRegistration) type = typedRegistration.ImplementationType; else if (registration is ISingleTypeRegistration singleTypeRegistration) type = singleTypeRegistration.InterfaceType; + else if (registration is IOpenGenericRegistration openGenericRegistration) + type = openGenericRegistration.ImplementationType; else throw new UnknownRegistrationException($"There is no registration {registration.GetType().Name} that can have lifestyle singleton."); @@ -331,7 +342,7 @@ namespace LightweightIocContainer return (T) instance; //if it doesn't already exist create a new instance and add it to the list - T newInstance = CreateInstance(registration, arguments, resolveStack); + T newInstance = CreateInstance(registration, arguments, resolveStack); _singletons.Add((type, newInstance)); return newInstance; @@ -363,13 +374,13 @@ namespace LightweightIocContainer if (instances.TryGetValue(scopeArgument, out object instance)) return (T) instance; - T createdInstance = CreateInstance(registration, arguments, resolveStack); + T createdInstance = CreateInstance(registration, arguments, resolveStack); instances.Add(scopeArgument, createdInstance); return createdInstance; } - T newInstance = CreateInstance(registration, arguments, resolveStack); + T newInstance = CreateInstance(registration, arguments, resolveStack); ConditionalWeakTable weakTable = new ConditionalWeakTable(); weakTable.Add(scopeArgument, newInstance); @@ -387,10 +398,10 @@ namespace LightweightIocContainer /// The constructor arguments /// The current resolve stack /// A newly created instance of the given - private T CreateInstance(IRegistrationBase registration, object[] arguments, List resolveStack) + private T CreateInstance(IRegistration registration, object[] arguments, List resolveStack) { - if (registration.Parameters != null) - arguments = UpdateArgumentsWithRegistrationParameters(registration, arguments); + if (registration is IWithParameters registrationWithParameters && registrationWithParameters.Parameters != null) + arguments = UpdateArgumentsWithRegistrationParameters(registrationWithParameters, arguments); T instance; if (registration is ITypedRegistrationBase defaultRegistration) @@ -411,6 +422,15 @@ namespace LightweightIocContainer else //factory method set to create the instance instance = singleTypeRegistration.FactoryMethod(this); } + else 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 throw new UnknownRegistrationException($"There is no registration of type {registration.GetType().Name}."); @@ -550,6 +570,24 @@ namespace LightweightIocContainer return null; } + [CanBeNull] + private IRegistration FindRegistration() + { + IRegistration registration = _registrations.FirstOrDefault(r => r.InterfaceType == typeof(T)); + if (registration != null) + return registration; + + //check for open generic registration + if (!typeof(T).GenericTypeArguments.Any()) + return null; + + List openGenericRegistrations = _registrations.Where(r => r.InterfaceType.ContainsGenericParameters).ToList(); + if (!openGenericRegistrations.Any()) + return null; + + return openGenericRegistrations.FirstOrDefault(r => r.InterfaceType == typeof(T).GetGenericTypeDefinition()); + } + /// /// Clear the multiton instances of the given from the registered multitons list /// @@ -570,7 +608,7 @@ namespace LightweightIocContainer /// /// The given /// True if the given is registered with this , false if not - public bool IsTypeRegistered() => _registrations.Any(registration => registration.InterfaceType == typeof(T)); + public bool IsTypeRegistered() => _registrations.Any(registration => registration.InterfaceType == typeof(T)); //TODO: Use FindRegistration<>()? /// /// The method diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml index b25a486..331e407 100644 --- a/LightweightIocContainer/LightweightIocContainer.xml +++ b/LightweightIocContainer/LightweightIocContainer.xml @@ -826,7 +826,7 @@ The given is not registered in this The registration for the given has an unknown - + Gets or creates a singleton instance of a given @@ -848,7 +848,7 @@ No arguments given Scope argument not given - + Creates an instance of a given