Skip to content

Commit bafde28

Browse files
committed
fix regression in init only fields
1 parent 3d32b6b commit bafde28

File tree

3 files changed

+50
-3
lines changed

3 files changed

+50
-3
lines changed

FastCloner.Tests/SpecialCaseTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3157,4 +3157,26 @@ public void LargeCircular_Test()
31573157
Assert.That(clonedNode.Data, Has.Count.EqualTo(2));
31583158
Assert.That(originalNode.Data[0], Is.Not.EqualTo(clonedNode.Data[1]));
31593159
}
3160+
3161+
[Test]
3162+
public void SelfReferenced_WithInitOnlyField_Test()
3163+
{
3164+
SelfReferencedWithInitOnlyField original = new SelfReferencedWithInitOnlyField
3165+
{
3166+
WithReadOnlyField = new ClassWithReadOnlyField()
3167+
};
3168+
3169+
SelfReferencedWithInitOnlyField clone = original.DeepClone();
3170+
3171+
Assert.That(clone, Is.Not.SameAs(original));
3172+
Assert.That(clone.WithReadOnlyField, Is.Not.SameAs(original.WithReadOnlyField));
3173+
Assert.That(clone.WithReadOnlyField.ReadOnlyValue, Is.EqualTo(original.WithReadOnlyField.ReadOnlyValue));
3174+
}
3175+
3176+
private class SelfReferencedWithInitOnlyField
3177+
{
3178+
public SelfReferencedWithInitOnlyField? Predecessor { get; set; }
3179+
3180+
public ClassWithReadOnlyField WithReadOnlyField { get; set; }
3181+
}
31603182
}

FastCloner/Code/ClonerToExprGenerator.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,22 @@ private static object GenerateProcessMethod(Type type, bool isDeepClone)
9191
}
9292
else
9393
{
94-
expressionList.Add(Expression.Assign(Expression.Field(toLocal, fieldInfo), Expression.Field(fromLocal, fieldInfo)));
94+
Expression sourceValue = Expression.Field(fromLocal, fieldInfo);
95+
if (fieldInfo.IsInitOnly)
96+
{
97+
ConstantExpression setter = Expression.Constant(FieldAccessorGenerator.GetFieldSetter(fieldInfo));
98+
expressionList.Add(
99+
Expression.Invoke(
100+
setter,
101+
Expression.Convert(toLocal, typeof(object)),
102+
Expression.Convert(sourceValue, typeof(object))
103+
)
104+
);
105+
}
106+
else
107+
{
108+
expressionList.Add(Expression.Assign(Expression.Field(toLocal, fieldInfo), sourceValue));
109+
}
95110
}
96111
}
97112

FastCloner/Code/FieldAccessorGenerator.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,20 @@ private static Action<object, object> CreateFieldSetter(FieldInfo field)
1818

1919
UnaryExpression targetCast = Expression.Convert(targetParam, field.DeclaringType);
2020
UnaryExpression valueCast = Expression.Convert(valueParam, field.FieldType);
21-
BinaryExpression assign = Expression.Assign(Expression.Field(targetCast, field), valueCast);
21+
Expression body;
22+
23+
if (field.IsInitOnly)
24+
{
25+
MethodInfo setValueMethod = typeof(FieldInfo).GetMethod(nameof(FieldInfo.SetValue), [typeof(object), typeof(object)])!;
26+
body = Expression.Call(Expression.Constant(field), setValueMethod, targetCast, valueCast);
27+
}
28+
else
29+
{
30+
body = Expression.Assign(Expression.Field(targetCast, field), valueCast);
31+
}
2232

2333
Expression<Action<object, object>> lambda = Expression.Lambda<Action<object, object>>(
24-
assign,
34+
body,
2535
targetParam,
2636
valueParam
2737
);

0 commit comments

Comments
 (0)