From 1f1524cf60006c3e8f4053c0f756560eef3b2174 Mon Sep 17 00:00:00 2001 From: Leandro Fernandes Vieira Date: Wed, 13 May 2020 23:46:51 -0300 Subject: [PATCH] adds logic to build new expression of types without parameter less constructor and tests --- .../ComplexTypeTests.cs | 35 +++++++++++++++++++ .../ComplexTypeExpressionFactory.cs | 28 ++++++++++++--- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/CloneExtensions.UnitTests/ComplexTypeTests.cs b/src/CloneExtensions.UnitTests/ComplexTypeTests.cs index c1e5061..dd34b9f 100644 --- a/src/CloneExtensions.UnitTests/ComplexTypeTests.cs +++ b/src/CloneExtensions.UnitTests/ComplexTypeTests.cs @@ -88,6 +88,23 @@ public void GetClone_CircularDependency_ItemsClonedCorrectly() Assert.AreSame(target.First, target.Second, "Are the same"); } + [TestMethod] + public void GetClone_ConstructorClass_ClonnedProperly() + { + var source = new ConstructorClass(null, 3, DateTime.Now, new List(), null, default) + { + PropertyA = 3, + PropertyB = Guid.NewGuid().ToString(), + PropertyC = DateTime.Now + }; + + var target = CloneFactory.GetClone(source); + + Assert.AreEqual(source.PropertyA, target.PropertyA); + Assert.AreEqual(source.PropertyB, target.PropertyB); + Assert.AreEqual(source.PropertyC, target.PropertyC); + } + [TestMethod] public void GetClone_DerivedTypeWithShadowedProperty_ClonnedProperly() { @@ -226,5 +243,23 @@ class DerivedClassOne : BaseClassOne // use the default implementation for VirtualProperty2 public override string VirtualProperty3 { get; set; } } + + class ConstructorClass + { + public ConstructorClass( + BaseClassOne a, + int? b, + DateTime c, + List d, + IInterface e, + SimpleStruct f) + { + + } + + public int PropertyA { get; set; } + public string PropertyB { get; set; } + public DateTime PropertyC { get; set; } + } } } diff --git a/src/CloneExtensions/ExpressionFactories/ComplexTypeExpressionFactory.cs b/src/CloneExtensions/ExpressionFactories/ComplexTypeExpressionFactory.cs index e226c86..c6a4f89 100644 --- a/src/CloneExtensions/ExpressionFactories/ComplexTypeExpressionFactory.cs +++ b/src/CloneExtensions/ExpressionFactories/ComplexTypeExpressionFactory.cs @@ -66,21 +66,39 @@ private Expression GetInitializationExpression() var funcInvokeCall = Expression.Call(dictIndex, "Invoke", null, Expression.Convert(Source, typeof(object))); var initializerCall = Expression.Convert(funcInvokeCall, _type); - // parameterless constructor - var constructor = _type.GetConstructor(new Type[0]); - return Expression.IfThenElse( containsKeyCall, Expression.Assign(Target, initializerCall), - (_type.IsAbstract() || _type.IsInterface() || (!_type.IsValueType() && constructor == null)) ? + (_type.IsAbstract() || _type.IsInterface()) ? Helpers.GetThrowInvalidOperationExceptionExpression(_type) : Expression.Assign( Target, - _type.IsValueType() ? (Expression)Source : Expression.New(_type) + _type.IsValueType() ? (Expression)Source : GetNewExpressionFor(_type) ) ); } + private static NewExpression GetNewExpressionFor(Type objType) + { + ConstructorInfo ctor = objType + .GetConstructors() + .OrderBy(x => x.GetParameters().Length) + .First(); + + return + Expression.New + ( + ctor, + ctor.GetParameters().Select(p => + p.IsOptional + ? Expression.Convert(Expression.Constant(p.DefaultValue), p.ParameterType) + : p.ParameterType.IsAbstract() || p.ParameterType.IsInterface() || + (p.ParameterType.IsValueType() && Nullable.GetUnderlyingType(p.ParameterType) == null) + ? Expression.Default(p.ParameterType) + : (Expression)GetNewExpressionFor(p.ParameterType)) + ); + } + private Expression GetFieldsCloneExpression(Func getItemCloneExpression) { var fields = from f in _type.GetTypeInfo().GetFields(BindingFlags.Public | BindingFlags.Instance)