Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/CloneExtensions.UnitTests/ComplexTypeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>(), 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()
{
Expand Down Expand Up @@ -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<string> d,
IInterface e,
SimpleStruct f)
{

}

public int PropertyA { get; set; }
public string PropertyB { get; set; }
public DateTime PropertyC { get; set; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Type, Expression, Expression> getItemCloneExpression)
{
var fields = from f in _type.GetTypeInfo().GetFields(BindingFlags.Public | BindingFlags.Instance)
Expand Down