1
0
Форкнуть 0

Add type checking to constructor parameters (#9)

This commit is contained in:
bytewizer 2022-08-04 11:21:49 -07:00 коммит произвёл GitHub
Родитель ed8bc6becb
Коммит 761be9e525
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 239 добавлений и 72 удалений

Просмотреть файл

@ -108,7 +108,10 @@ namespace nanoFramework.DependencyInjection
return lifetime + $"{nameof(ImplementationInstance)}: {ImplementationInstance}";
}
internal Type GetImplementationType()
/// <summary>
/// Returns the <see cref="Type"/> implementing the instance.
/// </summary>
public Type GetImplementationType()
{
if (ImplementationType != null)
{

Просмотреть файл

@ -36,9 +36,9 @@ namespace nanoFramework.DependencyInjection
/// </summary>
internal void DisposeServices()
{
for (int i = Services.Count - 1; i >= 0; i--)
for (int index = Services.Count - 1; index >= 0; index--)
{
if (Services[i].ImplementationInstance is IDisposable disposable)
if (Services[index].ImplementationInstance is IDisposable disposable)
{
disposable.Dispose();
}
@ -160,7 +160,8 @@ namespace nanoFramework.DependencyInjection
if (constructorParameters == null)
{
throw new InvalidOperationException();
throw new InvalidOperationException(
$"Constructor for '{implementationType}' could not be located.");
}
if (constructorParameters.Length == 0)
@ -176,10 +177,10 @@ namespace nanoFramework.DependencyInjection
{
var parameterType = constructorParameters[index].ParameterType;
if (TryBindToPrimitive(parameterType, out object defaultType))
if (parameterType.IsResolvable())
{
types[index] = parameterType;
parameters[index] = defaultType;
parameters[index] = GetResolvableDefault(parameterType);
}
else
{
@ -187,7 +188,8 @@ namespace nanoFramework.DependencyInjection
if (service == null)
{
throw new InvalidOperationException($"'{implementationType}'->'{parameterType}'.");
throw new InvalidOperationException(
$"Unable to resolve service for '{parameterType}'.");
}
types[index] = parameterType;
@ -220,18 +222,34 @@ namespace nanoFramework.DependencyInjection
{
if (constructors[j].GetParameters().Length == constructors[j + 1].GetParameters().Length)
{
throw new InvalidOperationException();
throw new InvalidOperationException(
$"Multiple constructors found in '{implementationType}'.");
}
}
}
// step 2: get the constructor with the most parameters
// step 2: get the constructor with the most resolvable parameters
foreach (ConstructorInfo constructor in constructors)
{
ParameterInfo[] parameters = constructor.GetParameters();
int length = parameters.Length;
foreach (ParameterInfo parameter in parameters)
{
Type type = parameter.ParameterType;
if (type.IsResolvable())
{
// check for simple binding first
}
else if (GetService(type) == null)
{
// binding could not be resolved ingore constructor
length = -1;
}
}
if (bestLength < length)
{
bestLength = length;
@ -243,49 +261,30 @@ namespace nanoFramework.DependencyInjection
}
/// <summary>
/// Try and bind to a primitive type.
/// Get primitive default type.
/// </summary>
private static bool TryBindToPrimitive(Type type, out object defaultType)
private static object GetResolvableDefault(Type type)
{
defaultType = null;
// This list dosn't match the binding list below because
// This list dosn't match the IsResolvable() because
// we only check for items we know are not null by default
if (type == typeof(object)) defaultType = default;
if (type == typeof(int)) defaultType = default(int);
if (type == typeof(uint)) defaultType = default(uint);
if (type == typeof(bool)) defaultType = default(bool);
if (type == typeof(char)) defaultType = default(char);
if (type == typeof(byte)) defaultType = default(byte);
if (type == typeof(sbyte)) defaultType = default(sbyte);
if (type == typeof(short)) defaultType = default(short);
if (type == typeof(ushort)) defaultType = default(ushort);
if (type == typeof(long)) defaultType = default(long);
if (type == typeof(ulong)) defaultType = default(ulong);
if (type == typeof(double)) defaultType = default(double);
if (type == typeof(Guid)) defaultType = default(Guid);
if (type == typeof(DateTime)) defaultType = default(DateTime);
if (type == typeof(TimeSpan)) defaultType = default(TimeSpan);
if (type == typeof(object)) return default;
if (type == typeof(int)) return default(int);
if (type == typeof(uint)) return default(uint);
if (type == typeof(bool)) return default(bool);
if (type == typeof(char)) return default(char);
if (type == typeof(byte)) return default(byte);
if (type == typeof(sbyte)) return default(sbyte);
if (type == typeof(short)) return default(short);
if (type == typeof(ushort)) return default(ushort);
if (type == typeof(long)) return default(long);
if (type == typeof(ulong)) return default(ulong);
if (type == typeof(double)) return default(double);
if (type == typeof(float)) return default(float);
if (type == typeof(Guid)) return default(Guid);
if (type == typeof(DateTime)) return default(DateTime);
if (type == typeof(TimeSpan)) return default(TimeSpan);
return type == typeof(object)
|| type == typeof(string)
|| type == typeof(int)
|| type == typeof(uint)
|| type == typeof(bool)
|| type == typeof(char)
|| type == typeof(byte)
|| type == typeof(sbyte)
|| type == typeof(short)
|| type == typeof(ushort)
|| type == typeof(long)
|| type == typeof(ulong)
|| type == typeof(double)
|| type == typeof(Guid)
|| type == typeof(DateTime)
|| type == typeof(TimeSpan)
|| type == typeof(Enum)
|| type == typeof(Array)
|| type == typeof(ArrayList);
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,37 @@
using System;
using System.Collections;
namespace nanoFramework.DependencyInjection
{
/// <summary>
/// Contains extension methods for <see cref="Type"/>.
/// </summary>
internal static class TypeExtensions
{
/// <summary>
/// Compares this instance to a specified type and returns an indication if resolvable value.
/// </summary>
/// <param name="type">The current <see cref="Type"/></param>
public static bool IsResolvable(this Type type)
{
return type == typeof(string)
|| type == typeof(bool)
|| type == typeof(byte)
|| type == typeof(sbyte)
|| type == typeof(short)
|| type == typeof(ushort)
|| type == typeof(int)
|| type == typeof(uint)
|| type == typeof(long)
|| type == typeof(ulong)
|| type == typeof(double)
|| type == typeof(float)
|| type == typeof(object)
|| type == typeof(DateTime)
|| type == typeof(TimeSpan)
|| type == typeof(Guid)
|| type == typeof(Array)
|| type == typeof(ArrayList);
}
}
}

Просмотреть файл

@ -32,6 +32,7 @@
<Compile Include="DependencyInjection\ActivatorUtilities.cs" />
<Compile Include="DependencyInjection\IServiceCollection.cs" />
<Compile Include="DependencyInjection\ServiceProviderEngine.cs" />
<Compile Include="DependencyInjection\TypeExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="DependencyInjection\ServiceCollection.cs" />
<Compile Include="DependencyInjection\ServiceCollectionContainerBuilderExtensions.cs" />

Просмотреть файл

@ -2,4 +2,4 @@
<packages>
<package id="nanoFramework.CoreLibrary" version="1.12.0" targetFramework="netnano1.0" />
<package id="Nerdbank.GitVersioning" version="3.5.107" developmentDependency="true" targetFramework="netnano1.0" />
</packages>
</packages>

Просмотреть файл

@ -43,9 +43,6 @@ namespace nanoFramework.DependencyInjection.UnitTests
// Assert.IsType(typeof(StructFakeService), service.GetType());
//}
//TODO: Reflection is not resolving with the IsArray flag
// https://github.com/nanoframework/Home/issues/1086
[TestMethod]
public void ServiceInstanceWithPrimitiveBinding()
{
@ -55,13 +52,28 @@ namespace nanoFramework.DependencyInjection.UnitTests
var service = (ClassWithPrimitiveBinding)serviceProvider.GetService(typeof(ClassWithPrimitiveBinding));
Assert.Null(service.Obj);
Assert.Null(service.Str);
Assert.Equal(0, service.Integer);
Assert.Equal(false, service.Boolean);
//Assert.Null(service.Characters); // array is not resolving correctly
Assert.Null(service.Obj);
Assert.NotNull(service.Guid);
Assert.NotNull(service.Time);
Assert.True(service.Boolean == false);
Assert.True(service.Short == 0);
Assert.True(service.Ushort == 0);
Assert.True(service.Int == 0);
Assert.True(service.UInt == 0);
Assert.True(service.Long == 0);
Assert.True(service.Ulong == 0);
Assert.True(service.Double == 0);
Assert.True(service.Float == 0);
Assert.True(service.Byte == new byte());
Assert.True(service.SByte == new sbyte());
Assert.True(service.DateTime == new DateTime());
Assert.True(service.TimeSpan == new TimeSpan());
Assert.True(service.Array == default);
Assert.True(service.ArrayList == default);
//TODO: Add array types - reflection is not resolving with the IsArray flag
// https://github.com/nanoframework/Home/issues/1086
}
[TestMethod]
@ -212,6 +224,19 @@ namespace nanoFramework.DependencyInjection.UnitTests
);
}
[TestMethod]
public void IngoreUnresolvedMultipleConstructorMatches()
{
var serviceProvider = new ServiceCollection()
.AddSingleton(typeof(IFakeService), typeof(FakeService))
.AddSingleton(typeof(ClassWithMultipleCtors))
.BuildServiceProvider();
var service = (ClassWithMultipleCtors)serviceProvider.GetService(typeof(ClassWithMultipleCtors));
Assert.Equal(2, service.CtorUsed);
}
[TestMethod]
public void SelfResolveThenDispose()
{

Просмотреть файл

@ -0,0 +1,50 @@
//
// Copyright (c) .NET Foundation and Contributors
// See LICENSE file in the project root for full license information.
//
using System.Collections;
namespace nanoFramework.DependencyInjection.UnitTests.Fakes
{
public class ClassWithMultipleCtors
{
public ClassWithMultipleCtors(string data)
{
CtorUsed = 0;
}
public ClassWithMultipleCtors(IFakeService service, string data)
{
CtorUsed = 1;
}
public ClassWithMultipleCtors(IFakeService service, string data1, int data2)
{
FakeService = service;
Data1 = data1;
Data2 = data2;
CtorUsed = 2;
}
public ClassWithMultipleCtors(IFakeService service, ICollection collection, string data1, int data2)
{
FakeService = service;
Data1 = data1;
Data2 = data2;
Collection = collection;
CtorUsed = 3;
}
public IFakeService FakeService { get; }
public string Data1 { get; }
public int Data2 { get; }
public int CtorUsed { get; set; }
public ICollection Collection { get; set; }
}
}

Просмотреть файл

@ -2,35 +2,86 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
namespace nanoFramework.DependencyInjection.UnitTests.Fakes
{
public class ClassWithPrimitiveBinding
{
public object Obj { get; set; }
public string Str { get; set; }
public int Integer { get; set; }
public bool Boolean { get; set; }
//public string[] Characters { get; set; }
public Guid Guid { get; set; }
public DateTime Time { get; set; }
public bool Boolean { get; set; }
public byte Byte { get; set; }
public sbyte SByte { get; set; }
public short Short { get; set; }
public ushort Ushort { get; set; }
public int Int { get; set; }
public uint UInt { get; set; }
public long Long { get; set; }
public ulong Ulong { get; set; }
public double Double { get; set; }
public float Float { get; set; }
public object Obj { get; set; }
public DateTime DateTime { get; set; }
public TimeSpan TimeSpan { get; set; }
public Guid Guid { get; set; }
public Array Array { get; set; }
public ArrayList ArrayList { get; set; }
public ClassWithPrimitiveBinding(
string str,
bool boolean,
byte @byte,
sbyte sByte,
short @short,
ushort @ushort,
int @int,
uint uInt,
long @long,
ulong @ulong,
double @double,
float @float,
object obj,
string str,
int integer,
bool boolean,
//string[] characters,
DateTime dateTime,
TimeSpan timeSpan,
Guid guid,
DateTime time)
Array array,
ArrayList arrayList)
{
Obj = obj;
Str = str;
Integer = integer;
Boolean = boolean;
//Characters = characters;
Byte = @byte;
SByte = sByte;
Short = @short;
Ushort = @ushort;
Int = @int;
UInt = uInt;
Long = @long;
Ulong = @ulong;
Double = @double;
Float = @float;
Obj = obj;
DateTime = dateTime;
TimeSpan = timeSpan;
Guid = guid;
Time = time;
Array = array;
ArrayList = arrayList;
}
}
}

Просмотреть файл

@ -29,6 +29,7 @@
<ItemGroup>
<Compile Include="ActivatorUtilitiesTests.cs" />
<Compile Include="DependencyInjectionTests.cs" />
<Compile Include="Fakes\ClassWithMultipleCtors.cs" />
<Compile Include="Fakes\ClassWithPrimitiveBinding.cs" />
<Compile Include="Fakes\CreationCountFakeService.cs" />
<Compile Include="Fakes\ClassWithThrowingEmptyCtor.cs" />