Valuetype bugfix (#1)
* 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:
Родитель
41b914fd51
Коммит
b1a495105e
|
@ -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)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче