diff --git a/LightweightIocContainer/Exceptions/GenericMethodNotFoundException.cs b/LightweightIocContainer/Exceptions/GenericMethodNotFoundException.cs
new file mode 100644
index 0000000..1438b54
--- /dev/null
+++ b/LightweightIocContainer/Exceptions/GenericMethodNotFoundException.cs
@@ -0,0 +1,24 @@
+// Author: Simon Gockner
+// Created: 2020-09-18
+// Copyright(c) 2020 SimonG. All Rights Reserved.
+
+using System;
+
+namespace LightweightIocContainer.Exceptions
+{
+ ///
+ /// Could not find generic method
+ ///
+ public class GenericMethodNotFoundException : Exception
+ {
+ ///
+ /// Could not find generic method
+ ///
+ /// The name of the generic method
+ public GenericMethodNotFoundException(string functionName)
+ : base($"Could not find function {functionName}")
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/LightweightIocContainer/GenericMethodCaller.cs b/LightweightIocContainer/GenericMethodCaller.cs
new file mode 100644
index 0000000..bff301d
--- /dev/null
+++ b/LightweightIocContainer/GenericMethodCaller.cs
@@ -0,0 +1,45 @@
+// Author: Simon Gockner
+// Created: 2020-09-18
+// Copyright(c) 2020 SimonG. All Rights Reserved.
+
+using System;
+using System.Reflection;
+using LightweightIocContainer.Exceptions;
+
+namespace LightweightIocContainer
+{
+ ///
+ /// Helper class to call a generic method without generic type parameters
+ ///
+ internal static class GenericMethodCaller
+ {
+ ///
+ /// Call a generic method without generic type parameters
+ ///
+ /// The caller of the method
+ /// The name of the method to call
+ /// The generic parameter as parameter
+ /// The to find the method
+ /// The parameters of the method
+ /// 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)
+ {
+ MethodInfo method = caller.GetType().GetMethod(functionName, bindingFlags);
+ MethodInfo genericMethod = method?.MakeGenericMethod(genericParameter);
+
+ if (genericMethod == null)
+ throw new GenericMethodNotFoundException(functionName);
+
+ try //exceptions thrown by methods called with invoke are wrapped into another exception, the exception thrown by the invoked method can be returned by `Exception.GetBaseException()`
+ {
+ return genericMethod.Invoke(caller, parameters);
+ }
+ catch (Exception ex)
+ {
+ throw ex.GetBaseException();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/LightweightIocContainer/Interfaces/IIocContainer.cs b/LightweightIocContainer/Interfaces/IIocContainer.cs
index 62a4434..d4c1465 100644
--- a/LightweightIocContainer/Interfaces/IIocContainer.cs
+++ b/LightweightIocContainer/Interfaces/IIocContainer.cs
@@ -29,6 +29,15 @@ namespace LightweightIocContainer.Interfaces
/// The created
IDefaultRegistration Register(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface;
+ ///
+ /// Register an open generic Interface with an open generic Type that implements it
+ ///
+ /// The open generic Interface to register
+ /// The open generic Type that implements the interface
+ /// The for this
+ /// The created
+ IOpenGenericRegistration RegisterOpenGenerics(Type tInterface, Type tImplementation, Lifestyle lifestyle = Lifestyle.Transient);
+
///
/// Register multiple interfaces for a that implements them
///
diff --git a/LightweightIocContainer/Interfaces/Registrations/IOpenGenericRegistration.cs b/LightweightIocContainer/Interfaces/Registrations/IOpenGenericRegistration.cs
new file mode 100644
index 0000000..4aaebc4
--- /dev/null
+++ b/LightweightIocContainer/Interfaces/Registrations/IOpenGenericRegistration.cs
@@ -0,0 +1,19 @@
+// Author: Simon Gockner
+// Created: 2020-09-18
+// Copyright(c) 2020 SimonG. All Rights Reserved.
+
+using System;
+
+namespace LightweightIocContainer.Interfaces.Registrations
+{
+ ///
+ /// for open generic types
+ ///
+ public interface IOpenGenericRegistration : IRegistration, ILifestyleProvider
+ {
+ ///
+ /// The that implements the that is registered with this
+ ///
+ Type ImplementationType { get; }
+ }
+}
\ No newline at end of file
diff --git a/LightweightIocContainer/IocContainer.cs b/LightweightIocContainer/IocContainer.cs
index 6f114be..c95a555 100644
--- a/LightweightIocContainer/IocContainer.cs
+++ b/LightweightIocContainer/IocContainer.cs
@@ -68,6 +68,29 @@ namespace LightweightIocContainer
return registration;
}
+ ///
+ /// Register an open generic Interface with an open generic Type that implements it
+ ///
+ /// The open generic Interface to register
+ /// The open generic Type that implements the interface
+ /// The for this
+ /// The created
+ /// Function can only be used to register open generic types
+ /// Can't register a multiton with open generic registration
+ 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;
+ }
+
///
/// Register multiple interfaces for a that implements them
///
@@ -246,20 +269,7 @@ namespace LightweightIocContainer
/// Could not find function
private object Resolve(Type type, object[] arguments, List resolveStack)
{
- MethodInfo resolveMethod = typeof(IocContainer).GetMethod(nameof(ResolveInternal), BindingFlags.NonPublic | BindingFlags.Instance);
- MethodInfo genericResolveMethod = resolveMethod?.MakeGenericMethod(type);
-
- if (genericResolveMethod == null)
- throw new InternalResolveException($"Could not find function {nameof(ResolveInternal)}");
-
- try //exceptions thrown by methods called with invoke are wrapped into another exception, the exception thrown by the invoked method can be returned by `Exception.GetBaseException()`
- {
- return genericResolveMethod.Invoke(this, new object[] { arguments, resolveStack });
- }
- catch (Exception ex)
- {
- throw ex.GetBaseException();
- }
+ return GenericMethodCaller.Call(this, nameof(ResolveInternal), type, BindingFlags.NonPublic | BindingFlags.Instance, arguments, resolveStack);
}
///
@@ -273,7 +283,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));
@@ -290,16 +300,23 @@ 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)
{
resolvedInstance = typedFactoryRegistration.Factory.Factory;
}
+ 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}.");
@@ -316,13 +333,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.");
@@ -332,7 +351,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;
@@ -364,13 +383,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
/// 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() => FindRegistration() != null;
///
/// The method
diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml
index b25a486..a2891e6 100644
--- a/LightweightIocContainer/LightweightIocContainer.xml
+++ b/LightweightIocContainer/LightweightIocContainer.xml
@@ -86,6 +86,17 @@
The constructor that does not match
+
+
+ Could not find generic method
+
+
+
+
+ Could not find generic method
+
+ The name of the generic method
+
The creation of the abstract method is illegal in its current state
@@ -274,6 +285,24 @@
The implemented abstract typed factory/>
+
+
+ Helper class to call a generic method without generic type parameters
+
+
+
+
+ Call a generic method without generic type parameters
+
+ The caller of the method
+ The name of the method to call
+ The generic parameter as parameter
+ The to find the method
+ The parameters of the method
+ The result of invoking the method
+ Could not find the generic method
+ Any thrown after invoking the generic method
+
An that installs all s for its given
@@ -346,6 +375,15 @@
The for this The created
+
+
+ Register an open generic Interface with an open generic Type that implements it
+
+ The open generic Interface to register
+ The open generic Type that implements the interface
+ The for this
+ The created
+
Register multiple interfaces for a that implements them
@@ -609,6 +647,16 @@
The registered interfaceThe registered implementation
+
+
+ for open generic types
+
+
+
+
+ The that implements the that is registered with this
+
+
The base registration that is used to register an Interface
@@ -705,6 +753,17 @@
The for this The created
+
+
+ Register an open generic Interface with an open generic Type that implements it
+
+ The open generic Interface to register
+ The open generic Type that implements the interface
+ The for this
+ The created
+ Function can only be used to register open generic types
+ Can't register a multiton with open generic registration
+
Register multiple interfaces for a that implements them
@@ -826,7 +885,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 +907,7 @@
No arguments givenScope argument not given
-
+
Creates an instance of a given
@@ -1078,6 +1137,39 @@
The of the multiton scope
+
+
+ for open generic types
+
+
+
+
+ for open generic types
+
+ The of the interface
+ The of the implementation type
+ The of this
+
+
+
+ The name of the
+
+
+
+
+ The of the Interface that is registered with this
+
+
+
+
+ The that implements the that is registered with this
+
+
+
+
+ The Lifestyle of Instances that are created with this
+
+
The that is used to register an Interface
diff --git a/LightweightIocContainer/Properties/AssemblyInfo.cs b/LightweightIocContainer/Properties/AssemblyInfo.cs
index 581d540..18bea16 100644
--- a/LightweightIocContainer/Properties/AssemblyInfo.cs
+++ b/LightweightIocContainer/Properties/AssemblyInfo.cs
@@ -4,8 +4,4 @@
using System.Runtime.CompilerServices;
-[assembly:InternalsVisibleTo("Test.LightweightIocContainer")]
-
-namespace LightweightIocContainer.Properties
-{
-}
\ No newline at end of file
+[assembly:InternalsVisibleTo("Test.LightweightIocContainer")]
\ No newline at end of file
diff --git a/LightweightIocContainer/Registrations/OpenGenericRegistration.cs b/LightweightIocContainer/Registrations/OpenGenericRegistration.cs
new file mode 100644
index 0000000..df37b4f
--- /dev/null
+++ b/LightweightIocContainer/Registrations/OpenGenericRegistration.cs
@@ -0,0 +1,50 @@
+// Author: Simon Gockner
+// Created: 2020-09-18
+// Copyright(c) 2020 SimonG. All Rights Reserved.
+
+using System;
+using LightweightIocContainer.Interfaces.Registrations;
+
+namespace LightweightIocContainer.Registrations
+{
+ ///
+ /// for open generic types
+ ///
+ public class OpenGenericRegistration : IOpenGenericRegistration
+ {
+ ///
+ /// for open generic types
+ ///
+ /// The of the interface
+ /// The of the implementation type
+ /// The of this
+ public OpenGenericRegistration(Type interfaceType, Type implementationType, Lifestyle lifestyle)
+ {
+ InterfaceType = interfaceType;
+ ImplementationType = implementationType;
+ Lifestyle = lifestyle;
+
+ Name = $"{InterfaceType.Name}, {ImplementationType.Name}, Lifestyle: {Lifestyle.ToString()}";
+ }
+
+ ///
+ /// The name of the
+ ///
+ public string Name { get; }
+
+ ///
+ /// The of the Interface that is registered with this
+ ///
+ public Type InterfaceType { get; }
+
+ ///
+ /// The that implements the that is registered with this
+ ///
+ public Type ImplementationType { get; }
+
+ ///
+ /// The Lifestyle of Instances that are created with this
+ ///
+ public Lifestyle Lifestyle { get; }
+ }
+}
\ No newline at end of file
diff --git a/LightweightIocContainer/Registrations/RegistrationFactory.cs b/LightweightIocContainer/Registrations/RegistrationFactory.cs
index f87383c..7ff2487 100644
--- a/LightweightIocContainer/Registrations/RegistrationFactory.cs
+++ b/LightweightIocContainer/Registrations/RegistrationFactory.cs
@@ -33,6 +33,11 @@ namespace LightweightIocContainer.Registrations
return new DefaultRegistration(typeof(TInterface), typeof(TImplementation), lifestyle);
}
+ public IOpenGenericRegistration Register(Type tInterface, Type tImplementation, Lifestyle lifestyle)
+ {
+ return new OpenGenericRegistration(tInterface, tImplementation, lifestyle);
+ }
+
///
/// Register multiple interfaces for a that implements them and create a
///
diff --git a/Test.LightweightIocContainer/EnumerableExtensionTest.cs b/Test.LightweightIocContainer/EnumerableExtensionTest.cs
index ddc724c..0540ac2 100644
--- a/Test.LightweightIocContainer/EnumerableExtensionTest.cs
+++ b/Test.LightweightIocContainer/EnumerableExtensionTest.cs
@@ -3,6 +3,7 @@
// Copyright(c) 2019 SimonG. All Rights Reserved.
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using LightweightIocContainer;
using NUnit.Framework;
@@ -27,6 +28,7 @@ namespace Test.LightweightIocContainer
[Test]
+ [SuppressMessage("ReSharper", "CollectionNeverUpdated.Local")]
public void TestFirstOrGivenNoPredicateEmpty()
{
List list = new List();
@@ -51,6 +53,7 @@ namespace Test.LightweightIocContainer
}
[Test]
+ [SuppressMessage("ReSharper", "CollectionNeverUpdated.Local")]
public void TestFirstOrGivenPredicateEmpty()
{
List list = new List();
diff --git a/Test.LightweightIocContainer/IocContainerRecursionTest.cs b/Test.LightweightIocContainer/IocContainerRecursionTest.cs
index 9a0c069..0e3e772 100644
--- a/Test.LightweightIocContainer/IocContainerRecursionTest.cs
+++ b/Test.LightweightIocContainer/IocContainerRecursionTest.cs
@@ -141,6 +141,7 @@ namespace Test.LightweightIocContainer
_iocContainer.Register();
IA a = _iocContainer.Resolve();
+ Assert.IsNotNull(a);
}
[Test]
diff --git a/Test.LightweightIocContainer/OpenGenericRegistrationTest.cs b/Test.LightweightIocContainer/OpenGenericRegistrationTest.cs
new file mode 100644
index 0000000..e1b87d7
--- /dev/null
+++ b/Test.LightweightIocContainer/OpenGenericRegistrationTest.cs
@@ -0,0 +1,70 @@
+// Author: Simon Gockner
+// Created: 2020-09-18
+// Copyright(c) 2020 SimonG. All Rights Reserved.
+
+using System.Diagnostics.CodeAnalysis;
+using JetBrains.Annotations;
+using LightweightIocContainer;
+using LightweightIocContainer.Exceptions;
+using LightweightIocContainer.Interfaces;
+using NUnit.Framework;
+
+namespace Test.LightweightIocContainer
+{
+ [TestFixture]
+ public class OpenGenericRegistrationTest
+ {
+ private IIocContainer _iocContainer;
+
+ [UsedImplicitly]
+ [SuppressMessage("ReSharper", "UnusedTypeParameter")]
+ public interface ITest
+ {
+
+ }
+
+ [UsedImplicitly]
+ public class Test : ITest
+ {
+
+ }
+
+ [SetUp]
+ public void SetUp() => _iocContainer = new IocContainer();
+
+ [TearDown]
+ public void TearDown() => _iocContainer.Dispose();
+
+ [Test]
+ public void TestRegisterOpenGenericType()
+ {
+ _iocContainer.RegisterOpenGenerics(typeof(ITest<>), typeof(Test<>));
+
+ ITest test = _iocContainer.Resolve>();
+ Assert.NotNull(test);
+ }
+
+ [Test]
+ public void TestRegisterOpenGenericTypeAsSingleton()
+ {
+ _iocContainer.RegisterOpenGenerics(typeof(ITest<>), typeof(Test<>), Lifestyle.Singleton);
+
+ ITest test = _iocContainer.Resolve>();
+ Assert.NotNull(test);
+
+ ITest secondTest = _iocContainer.Resolve>();
+ Assert.NotNull(secondTest);
+
+ Assert.AreEqual(test, secondTest);
+ Assert.AreSame(test, secondTest);
+ }
+
+ [Test]
+ public void TestRegisterOpenGenericTypeAsMultitonThrowsException()
+ => Assert.Throws(() => _iocContainer.RegisterOpenGenerics(typeof(ITest<>), typeof(Test<>), Lifestyle.Multiton));
+
+ [Test]
+ public void TestRegisterNonOpenGenericTypeWithOpenGenericsFunctionThrowsException()
+ => Assert.Throws(() => _iocContainer.RegisterOpenGenerics(typeof(int), typeof(int)));
+ }
+}
\ No newline at end of file