#14: fix handling of default parameters

pull/32/head
Simon Gockner 7 years ago
parent 42b9eb53c9
commit 19d4ebcff4
  1. 60
      LightweightIocContainer/EnumerableExtension.cs
  2. 39
      LightweightIocContainer/IocContainer.cs
  3. 41
      LightweightIocContainer/LightweightIocContainer.xml
  4. 18
      LightweightIocContainer/TypeExtension.cs
  5. 22
      Test.LightweightIocContainer/IocContainerTest.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
{
/// <summary>
/// Returns the first element of a <see cref="IEnumerable{T}"/>, or a new instance of a given <see cref="Type"/> if the <see cref="IEnumerable{T}"/> contains no elements
/// </summary>
/// <typeparam name="TSource">The source <see cref="Type"/> of the <see cref="IEnumerable{T}"/></typeparam>
/// <typeparam name="TGiven">The given <see cref="Type"/> to return if the <see cref="IEnumerable{T}"/> contains no elements</typeparam>
/// <param name="source">The given <see cref="IEnumerable{T}"/></param>
/// <returns>The first element of the <see cref="IEnumerable{T}"/>, or a new instance of a given <see cref="Type"/> if the <see cref="IEnumerable{T}"/> contains no elements</returns>
public static TSource FirstOrGiven<TSource, TGiven>(this IEnumerable<TSource> source) where TGiven : TSource, new() =>
source.TryGetFirst<TSource, TGiven>(null);
/// <summary>
/// Returns the first element of a <see cref="IEnumerable{T}"/> that satisfies a condition, or a new instance of a given <see cref="Type"/> if no such element is found
/// </summary>
/// <typeparam name="TSource">The source <see cref="Type"/> of the <see cref="IEnumerable{T}"/></typeparam>
/// <typeparam name="TGiven">The given <see cref="Type"/> to return if the <see cref="IEnumerable{T}"/> contains no element that satisfies the given condition</typeparam>
/// <param name="source">The given <see cref="IEnumerable{T}"/></param>
/// <param name="predicate">A function to test each element for a condition</param>
/// <returns>The first element of the <see cref="IEnumerable{T}"/> that satisfies a condition, or a new instance of the given <see cref="Type"/> if no such element is found</returns>
public static TSource FirstOrGiven<TSource, TGiven>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) where TGiven : TSource, new() =>
source.TryGetFirst<TSource, TGiven>(predicate);
/// <summary>
/// Tries to get the first element of the given <see cref="IEnumerable{T}"/> or creates a new element of a given <see cref="Type"/> when no element is found
/// </summary>
/// <typeparam name="TSource">The source <see cref="Type"/> of the <see cref="IEnumerable{T}"/></typeparam>
/// <typeparam name="TGiven">The given <see cref="Type"/> to create a new element when no fitting element is found</typeparam>
/// <param name="source">The given <see cref="IEnumerable{T}"/></param>
/// <param name="predicate">A function to test each element for a condition</param>
/// <returns>The first element of the <see cref="IEnumerable{T}"/> or a new instance of the given <see cref="Type"/> when no element is found</returns>
private static TSource TryGetFirst<TSource, TGiven>(this IEnumerable<TSource> source, Func<TSource, bool> 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();
}
}
}
}

@ -80,7 +80,7 @@ namespace LightweightIocContainer
/// <param name="arguments">The constructor arguments</param>
/// <returns>An instance of the given type</returns>
/// <exception cref="InternalResolveException">Could not find function <see cref="ResolveInternal{T}"/></exception>
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<object, InternalResolvePlaceholder>(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<object, InternalResolvePlaceholder>(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();
}
/// <summary>
/// An internal placeholder that is used during the resolving process
/// </summary>
private class InternalResolvePlaceholder
{
}
}
}

@ -4,6 +4,35 @@
<name>LightweightIocContainer</name>
</assembly>
<members>
<member name="M:LightweightIocContainer.EnumerableExtension.FirstOrGiven``2(System.Collections.Generic.IEnumerable{``0})">
<summary>
Returns the first element of a <see cref="T:System.Collections.Generic.IEnumerable`1"/>, or a new instance of a given <see cref="T:System.Type"/> if the <see cref="T:System.Collections.Generic.IEnumerable`1"/> contains no elements
</summary>
<typeparam name="TSource">The source <see cref="T:System.Type"/> of the <see cref="T:System.Collections.Generic.IEnumerable`1"/></typeparam>
<typeparam name="TGiven">The given <see cref="T:System.Type"/> to return if the <see cref="T:System.Collections.Generic.IEnumerable`1"/> contains no elements</typeparam>
<param name="source">The given <see cref="T:System.Collections.Generic.IEnumerable`1"/></param>
<returns>The first element of the <see cref="T:System.Collections.Generic.IEnumerable`1"/>, or a new instance of a given <see cref="T:System.Type"/> if the <see cref="T:System.Collections.Generic.IEnumerable`1"/> contains no elements</returns>
</member>
<member name="M:LightweightIocContainer.EnumerableExtension.FirstOrGiven``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,System.Boolean})">
<summary>
Returns the first element of a <see cref="T:System.Collections.Generic.IEnumerable`1"/> that satisfies a condition, or a new instance of a given <see cref="T:System.Type"/> if no such element is found
</summary>
<typeparam name="TSource">The source <see cref="T:System.Type"/> of the <see cref="T:System.Collections.Generic.IEnumerable`1"/></typeparam>
<typeparam name="TGiven">The given <see cref="T:System.Type"/> to return if the <see cref="T:System.Collections.Generic.IEnumerable`1"/> contains no element that satisfies the given condition</typeparam>
<param name="source">The given <see cref="T:System.Collections.Generic.IEnumerable`1"/></param>
<param name="predicate">A function to test each element for a condition</param>
<returns>The first element of the <see cref="T:System.Collections.Generic.IEnumerable`1"/> that satisfies a condition, or a new instance of the given <see cref="T:System.Type"/> if no such element is found</returns>
</member>
<member name="M:LightweightIocContainer.EnumerableExtension.TryGetFirst``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,System.Boolean})">
<summary>
Tries to get the first element of the given <see cref="T:System.Collections.Generic.IEnumerable`1"/> or creates a new element of a given <see cref="T:System.Type"/> when no element is found
</summary>
<typeparam name="TSource">The source <see cref="T:System.Type"/> of the <see cref="T:System.Collections.Generic.IEnumerable`1"/></typeparam>
<typeparam name="TGiven">The given <see cref="T:System.Type"/> to create a new element when no fitting element is found</typeparam>
<param name="source">The given <see cref="T:System.Collections.Generic.IEnumerable`1"/></param>
<param name="predicate">A function to test each element for a condition</param>
<returns>The first element of the <see cref="T:System.Collections.Generic.IEnumerable`1"/> or a new instance of the given <see cref="T:System.Type"/> when no element is found</returns>
</member>
<member name="T:LightweightIocContainer.Exceptions.IllegalAbstractMethodCreationException">
<summary>
The creation of the abstract method is illegal in its current state
@ -352,6 +381,11 @@
</summary>
<typeparam name="T">The Type to clear the multiton instances</typeparam>
</member>
<member name="T:LightweightIocContainer.IocContainer.InternalResolvePlaceholder">
<summary>
An internal placeholder that is used during the resolving process
</summary>
</member>
<member name="T:LightweightIocContainer.Lifestyle">
<summary>
The Lifestyles that can be used for a <see cref="T:LightweightIocContainer.Interfaces.Registrations.IDefaultRegistration`1"/>
@ -507,5 +541,12 @@
<exception cref="T:LightweightIocContainer.Exceptions.InvalidFactoryRegistrationException">Factory registration is invalid</exception>
<exception cref="T:LightweightIocContainer.Exceptions.IllegalAbstractMethodCreationException">Creation of abstract methods are illegal in their current state</exception>
</member>
<member name="M:LightweightIocContainer.TypeExtension.GetDefault(System.Type)">
<summary>
Returns the default value for a given <see cref="T:System.Type"/>
</summary>
<param name="type">The given <see cref="T:System.Type"/></param>
<returns>The default value for the given <see cref="T:System.Type"/></returns>
</member>
</members>
</doc>

@ -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
{
/// <summary>
/// Returns the default value for a given <see cref="Type"/>
/// </summary>
/// <param name="type">The given <see cref="Type"/></param>
/// <returns>The default value for the given <see cref="Type"/></returns>
public static object GetDefault(this Type type) => type.IsValueType ? Activator.CreateInstance(type) : null;
}
}

@ -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<T>();
}
@ -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<TestConstructor>(createdTest);
}
[Test]
public void TestResolveFromFactoryWithDefaultParamCreate()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, TestConstructor>());
iocContainer.Register(RegistrationFactory.Register<Test, Test>()); //this registration is abnormal and should only be used in unit tests
iocContainer.Register(RegistrationFactory.RegisterFactory<ITestFactory>(iocContainer));
ITestFactory testFactory = iocContainer.Resolve<ITestFactory>();
ITest createdTest = testFactory.Create(true);
Assert.IsInstanceOf<TestConstructor>(createdTest);
}
[Test]
public void TestResolveMultitonFromFactory()
{
@ -309,6 +330,7 @@ namespace Test.LightweightIocContainer
public void TestResolveMultitonFromFactoryClearInstances()
{
IIocContainer iocContainer = new IocContainer();
iocContainer.Register(RegistrationFactory.Register<ITest, Test, MultitonScope>());
iocContainer.Register(RegistrationFactory.RegisterFactory<ITestFactory>(iocContainer));

Loading…
Cancel
Save