diff --git a/LightweightIocContainer/EnumerableExtension.cs b/LightweightIocContainer/EnumerableExtension.cs new file mode 100644 index 0000000..99f150c --- /dev/null +++ b/LightweightIocContainer/EnumerableExtension.cs @@ -0,0 +1,60 @@ +// // Author: Gockner, Simon +// // Created: 2019-07-02 +// // Copyright(c) 2019 SimonG. All Rights Reserved. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace LightweightIocContainer +{ + internal static class EnumerableExtension + { + /// + /// Returns the first element of a , or a new instance of a given if the contains no elements + /// + /// The source of the + /// The given to return if the contains no elements + /// The given + /// The first element of the , or a new instance of a given if the contains no elements + public static TSource FirstOrGiven(this IEnumerable source) where TGiven : TSource, new() => + source.TryGetFirst(null); + + /// + /// Returns the first element of a that satisfies a condition, or a new instance of a given if no such element is found + /// + /// The source of the + /// The given to return if the contains no element that satisfies the given condition + /// The given + /// A function to test each element for a condition + /// The first element of the that satisfies a condition, or a new instance of the given if no such element is found + public static TSource FirstOrGiven(this IEnumerable source, Func predicate) where TGiven : TSource, new() => + source.TryGetFirst(predicate); + + /// + /// Tries to get the first element of the given or creates a new element of a given when no element is found + /// + /// The source of the + /// The given to create a new element when no fitting element is found + /// The given + /// A function to test each element for a condition + /// The first element of the or a new instance of the given when no element is found + private static TSource TryGetFirst(this IEnumerable source, Func predicate) where TGiven : TSource, new() + { + try + { + TSource first; + if (predicate == null) + first = source.First(); + else + first = source.First(predicate); + + return first; + } + catch (Exception) + { + return new TGiven(); + } + } + } +} \ No newline at end of file diff --git a/LightweightIocContainer/IocContainer.cs b/LightweightIocContainer/IocContainer.cs index 7009119..256d929 100644 --- a/LightweightIocContainer/IocContainer.cs +++ b/LightweightIocContainer/IocContainer.cs @@ -80,7 +80,7 @@ namespace LightweightIocContainer /// The constructor arguments /// An instance of the given type /// Could not find function - public object Resolve(Type type, object[] arguments) //somehow the order of the arguments is different in the application compared to the unit test + public object Resolve(Type type, object[] arguments) { MethodInfo resolveMethod = typeof(IocContainer).GetMethod(nameof(ResolveInternal), BindingFlags.NonPublic | BindingFlags.Instance); MethodInfo genericResolveMethod = resolveMethod?.MakeGenericMethod(type); @@ -218,18 +218,37 @@ namespace LightweightIocContainer ParameterInfo[] parameters = ctor.GetParameters(); foreach (var parameter in parameters) { - object fittingArgument = null; + object fittingArgument = new InternalResolvePlaceholder(); if (argumentsList != null) { - fittingArgument = argumentsList.FirstOrDefault(a => a?.GetType() == parameter.ParameterType); - if (fittingArgument != null) + fittingArgument = argumentsList.FirstOrGiven(a => a?.GetType() == parameter.ParameterType); + if (!(fittingArgument is InternalResolvePlaceholder)) { int index = argumentsList.IndexOf(fittingArgument); - argumentsList[index] = null; + argumentsList[index] = new InternalResolvePlaceholder(); + } + else //fittingArgument is InternalResolvePlaceholder + { + try + { + fittingArgument = Resolve(parameter.ParameterType, null); + } + catch (Exception) + { + fittingArgument = argumentsList.FirstOrGiven(a => parameter.ParameterType.GetDefault() == a); + + if (!(fittingArgument is InternalResolvePlaceholder)) + { + int index = argumentsList.IndexOf(fittingArgument); + argumentsList[index] = new InternalResolvePlaceholder(); + } + } } } - if (fittingArgument == null) + if (fittingArgument is InternalResolvePlaceholder && parameter.HasDefaultValue) + ctorParams.Add(parameter.DefaultValue); + else if (fittingArgument is InternalResolvePlaceholder) ctorParams.Add(Resolve(parameter.ParameterType, null)); else ctorParams.Add(fittingArgument); @@ -267,5 +286,13 @@ namespace LightweightIocContainer _singletons.Clear(); _multitons.Clear(); } + + /// + /// An internal placeholder that is used during the resolving process + /// + private class InternalResolvePlaceholder + { + + } } } \ No newline at end of file diff --git a/LightweightIocContainer/LightweightIocContainer.xml b/LightweightIocContainer/LightweightIocContainer.xml index 71c031c..0fdeec7 100644 --- a/LightweightIocContainer/LightweightIocContainer.xml +++ b/LightweightIocContainer/LightweightIocContainer.xml @@ -4,6 +4,35 @@ LightweightIocContainer + + + Returns the first element of a , or a new instance of a given if the contains no elements + + The source of the + The given to return if the contains no elements + The given + The first element of the , or a new instance of a given if the contains no elements + + + + Returns the first element of a that satisfies a condition, or a new instance of a given if no such element is found + + The source of the + The given to return if the contains no element that satisfies the given condition + The given + A function to test each element for a condition + The first element of the that satisfies a condition, or a new instance of the given if no such element is found + + + + Tries to get the first element of the given or creates a new element of a given when no element is found + + The source of the + The given to create a new element when no fitting element is found + The given + A function to test each element for a condition + The first element of the or a new instance of the given when no element is found + The creation of the abstract method is illegal in its current state @@ -352,6 +381,11 @@ The Type to clear the multiton instances + + + An internal placeholder that is used during the resolving process + + The Lifestyles that can be used for a @@ -507,5 +541,12 @@ Factory registration is invalid Creation of abstract methods are illegal in their current state + + + Returns the default value for a given + + The given + The default value for the given + diff --git a/LightweightIocContainer/TypeExtension.cs b/LightweightIocContainer/TypeExtension.cs new file mode 100644 index 0000000..5499448 --- /dev/null +++ b/LightweightIocContainer/TypeExtension.cs @@ -0,0 +1,18 @@ +// // Author: Gockner, Simon +// // Created: 2019-07-01 +// // Copyright(c) 2019 SimonG. All Rights Reserved. + +using System; + +namespace LightweightIocContainer +{ + internal static class TypeExtension + { + /// + /// Returns the default value for a given + /// + /// The given + /// The default value for the given + public static object GetDefault(this Type type) => type.IsValueType ? Activator.CreateInstance(type) : null; + } +} \ No newline at end of file diff --git a/Test.LightweightIocContainer/IocContainerTest.cs b/Test.LightweightIocContainer/IocContainerTest.cs index 13052a1..1f26bfd 100644 --- a/Test.LightweightIocContainer/IocContainerTest.cs +++ b/Test.LightweightIocContainer/IocContainerTest.cs @@ -1,3 +1,4 @@ +using System; using JetBrains.Annotations; using LightweightIocContainer; using LightweightIocContainer.Exceptions; @@ -26,6 +27,7 @@ namespace Test.LightweightIocContainer ITest Create(); ITest Create(string name); ITest Create(MultitonScope scope); + ITest Create(bool someBool, string name = null); void ClearMultitonInstance(); } @@ -54,6 +56,11 @@ namespace Test.LightweightIocContainer { } + + public TestConstructor(bool someBool, Test test, string name) + { + + } } public class MultitonScope @@ -284,6 +291,20 @@ namespace Test.LightweightIocContainer Assert.IsInstanceOf(createdTest); } + [Test] + public void TestResolveFromFactoryWithDefaultParamCreate() + { + IIocContainer iocContainer = new IocContainer(); + iocContainer.Register(RegistrationFactory.Register()); + iocContainer.Register(RegistrationFactory.Register()); //this registration is abnormal and should only be used in unit tests + iocContainer.Register(RegistrationFactory.RegisterFactory(iocContainer)); + + ITestFactory testFactory = iocContainer.Resolve(); + ITest createdTest = testFactory.Create(true); + + Assert.IsInstanceOf(createdTest); + } + [Test] public void TestResolveMultitonFromFactory() { @@ -309,6 +330,7 @@ namespace Test.LightweightIocContainer public void TestResolveMultitonFromFactoryClearInstances() { IIocContainer iocContainer = new IocContainer(); + iocContainer.Register(RegistrationFactory.Register()); iocContainer.Register(RegistrationFactory.RegisterFactory(iocContainer));