* Add value type test cases

* Fix runtime value type

* Fix design time value type

* Tidying

* Remove tail call optimisation

* Remove argument count

* Removing unused Lambdas

Co-authored-by: Steve Wilkes <steve@agileobjects.co.uk>
This commit is contained in:
randomC0der 2021-04-08 17:08:47 +00:00 коммит произвёл GitHub
Родитель 41b914fd51
Коммит b1a495105e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 105 добавлений и 30 удалений

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

@ -15,6 +15,24 @@ namespace CreateInstanceFromType.Tests
Assert.NotNull(instance);
}
[Fact]
public void ShouldUseAParameterlessValueTypeCtor()
{
var instance = (Guid)CreateInstanceFromType2020RuntimeArgs
.GetInstance(typeof(Guid));
Assert.Equal(default, instance);
}
[Fact]
public void ShouldUseAParameterlessValueTypeCtorDesign()
{
var instance = (Guid)CreateInstanceFromType2020DesignTimeArgs
.GetInstance(typeof(Guid));
Assert.Equal(default, instance);
}
[Fact]
public void ShouldUseASingleParameterCtor()
{
@ -24,6 +42,24 @@ namespace CreateInstanceFromType.Tests
Assert.NotNull(instance);
Assert.Equal("hello!", instance.Value);
}
[Fact]
public void ShouldUseASingleParameterValueTypeCtor()
{
var instance = (Guid)CreateInstanceFromType2020RuntimeArgs
.GetInstance(typeof(Guid), "5e55498a-86e1-495c-b829-0c5170346ef5");
Assert.Equal(Guid.Parse("5e55498a-86e1-495c-b829-0c5170346ef5"), instance);
}
[Fact]
public void ShouldUseASingleParameterValueTypeCtorDesign()
{
var instance = (Guid)CreateInstanceFromType2020DesignTimeArgs
.GetInstance(typeof(Guid), "5e55498a-86e1-495c-b829-0c5170346ef5");
Assert.Equal(Guid.Parse("5e55498a-86e1-495c-b829-0c5170346ef5"), instance);
}
[Fact]
public void ShouldUseATwoParameterCtor()

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

@ -93,11 +93,14 @@ namespace CreateInstanceFromType
{
return _factoriesByType.GetOrAdd(type, t =>
{
// The default, parameterless constructor:
var ctor = t.GetConstructor(Type.EmptyTypes);
// An Expression representing the default constructor call:
var instanceCreation = Expression.New(ctor);
Expression instanceCreation = Expression.New(t);
if (t.IsValueType)
{
// A value type needs additional boxing:
instanceCreation = Expression.Convert(instanceCreation, typeof(object));
}
// Compile the Expression into a Func which returns the
// constructed object:
@ -124,14 +127,20 @@ namespace CreateInstanceFromType
// The matching constructor:
var ctor = t.GetConstructor(new[] { argType });
// An Expression representing the parameter to
// pass to the Func and constructor:
var argument = Expression.Parameter(argType, "param");
// An Expression representing the constructor call,
// passing in the constructor parameter:
var instanceCreation = Expression.New(ctor, argument);
Expression instanceCreation = Expression.New(ctor, argument);
if (t.IsValueType)
{
// A value type needs additional boxing:
instanceCreation = Expression.Convert(instanceCreation, typeof(object));
}
// Compile the Expression into a Func which takes one
// argument and returns the constructed object:
@ -159,7 +168,7 @@ namespace CreateInstanceFromType
// The matching constructor:
var ctor = t.GetConstructor(new[] { arg1Type, arg2Type });
// A set of Expressions representing the parameters to
// pass to the Func and constructor:
var argument1 = Expression.Parameter(arg1Type, "param1");
@ -167,9 +176,15 @@ namespace CreateInstanceFromType
// An Expression representing the constructor call,
// passing in the constructor parameters:
var instanceCreation = Expression
Expression instanceCreation = Expression
.New(ctor, argument1, argument2);
if (t.IsValueType)
{
// A value type needs additional boxing:
instanceCreation = Expression.Convert(instanceCreation, typeof(object));
}
// Compile the Expression into a Func which takes two
// arguments and returns the constructed object:
var instanceCreationLambda = Expression
@ -197,7 +212,7 @@ namespace CreateInstanceFromType
// The matching constructor:
var ctor = t.GetConstructor(new[] { arg1Type, arg2Type, arg3Type });
// A set of Expressions representing the parameters to
// pass to the Func and constructor:
var argument1 = Expression.Parameter(arg1Type, "param1");
@ -206,9 +221,15 @@ namespace CreateInstanceFromType
// An Expression representing the constructor call,
// passing in the constructor parameters:
var instanceCreation = Expression
Expression instanceCreation = Expression
.New(ctor, argument1, argument2, argument3);
if (t.IsValueType)
{
// A value type needs additional boxing:
instanceCreation = Expression.Convert(instanceCreation, typeof(object));
}
// Compile the Expression into a Func which takes three
// arguments and returns the constructed object:
var instanceCreationLambda = Expression

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

@ -27,16 +27,43 @@
private static Func<object[], object> CreateObjectFactory(TypeFactoryKey key)
{
var argumentTypes = key.ArgumentTypes;
var argumentCount = argumentTypes.Length;
// The constructor which matches the given argument types:
var instanceTypeCtor = GetMatchingConstructor(key, argumentTypes, argumentCount);
// An Expression representing the parameter to pass
// to the Func:
var lambdaParameters = Expression.Parameter(typeof(object[]), "params");
// Get an Expression representing the 'new' constructor call:
var instanceCreation = GetInstanceCreation(key, lambdaParameters);
if (key.Type.IsValueType)
{
// A value type needs additional boxing:
instanceCreation = Expression
.Convert(instanceCreation, typeof(object));
}
// Compile the Expression into a Func which takes an
// object argument array and returns the constructed object:
var instanceCreationLambda = Expression
.Lambda<Func<object[], object>>(instanceCreation, lambdaParameters);
return instanceCreationLambda.Compile();
}
private static Expression GetInstanceCreation(
TypeFactoryKey key,
Expression lambdaParameters)
{
var argumentTypes = key.ArgumentTypes;
var argumentCount = argumentTypes.Length;
if (key.Type.IsValueType && argumentCount == 0)
{
return Expression.New(key.Type);
}
// The constructor which matches the given argument types:
var instanceTypeCtor = GetMatchingConstructor(key, argumentTypes);
// A set of Expressions representing the parameters to pass
// to the constructor:
var ctorArguments = new Expression[argumentCount];
@ -58,35 +85,26 @@
// An Expression representing the constructor call,
// passing in the constructor parameters:
var instanceCreation = Expression.New(instanceTypeCtor, ctorArguments);
// Compile the Expression into a Func which takes an
// object argument array and returns the constructed object:
var instanceCreationLambda = Expression
.Lambda<Func<object[], object>>(instanceCreation, lambdaParameters);
return instanceCreationLambda.Compile();
return Expression.New(instanceTypeCtor, ctorArguments);
}
private static ConstructorInfo GetMatchingConstructor(
TypeFactoryKey key,
Type[] argumentTypes,
int argumentCount)
private static ConstructorInfo GetMatchingConstructor(TypeFactoryKey key, Type[] argumentTypes)
{
if (!key.HasNullArgumentTypes)
{
return key.Type.GetConstructor(
Public | Instance,
binder: null,
Type.DefaultBinder,
CallingConventions.HasThis,
argumentTypes,
new ParameterModifier[0]) ??
Array.Empty<ParameterModifier>()) ??
throw new NotSupportedException("Failed to find a matching constructor");
}
var constructors = key.Type.GetConstructors(Public | Instance);
var matchingCtor = default(ConstructorInfo);
var parameters = default(ParameterInfo[]);
var argumentCount = argumentTypes.Length;
for (int i = 0, l = constructors.Length; i < l; ++i)
{