Skip to content

Commit d0951ae

Browse files
author
Maxim Becker
committed
fix regression in classes with multiple init only fields/properties
1 parent 0d778e6 commit d0951ae

File tree

3 files changed

+32
-3
lines changed

3 files changed

+32
-3
lines changed

FastCloner.Tests/SpecialCaseTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3235,4 +3235,33 @@ private class ClassWithWritableValueTypeField
32353235
private decimal readOnlyField = 1m;
32363236
public decimal ReadOnlyValue => readOnlyField;
32373237
}
3238+
3239+
[Test]
3240+
public void SelfReferenced_WithMultipleReadOnlyProperties_Test()
3241+
{
3242+
SelfReferencedWithMultipleReadOnlyProperties original = new SelfReferencedWithMultipleReadOnlyProperties
3243+
{
3244+
WithMultipleReadOnlyProperties = new ClassWithMultipleReadOnlyProperties()
3245+
};
3246+
3247+
SelfReferencedWithMultipleReadOnlyProperties clone = original.DeepClone();
3248+
3249+
Assert.That(clone, Is.Not.SameAs(original));
3250+
Assert.That(clone.WithMultipleReadOnlyProperties, Is.Not.SameAs(original.WithMultipleReadOnlyProperties));
3251+
Assert.That(clone.WithMultipleReadOnlyProperties.Name, Is.EqualTo(original.WithMultipleReadOnlyProperties.Name));
3252+
Assert.That(clone.WithMultipleReadOnlyProperties.Id, Is.EqualTo(original.WithMultipleReadOnlyProperties.Id));
3253+
}
3254+
3255+
private class SelfReferencedWithMultipleReadOnlyProperties
3256+
{
3257+
public SelfReferencedWithMultipleReadOnlyProperties? Predecessor { get; set; }
3258+
3259+
public ClassWithMultipleReadOnlyProperties WithMultipleReadOnlyProperties { get; set; }
3260+
}
3261+
3262+
private class ClassWithMultipleReadOnlyProperties
3263+
{
3264+
public int Id { get; } = 1;
3265+
public string Name { get; } = "Test";
3266+
}
32383267
}

FastCloner/Code/FastClonerCache.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ internal static bool IsTypeIgnored(Type type)
1717
private static readonly ClrCache<object> deepClassToCache = new ClrCache<object>();
1818
private static readonly ClrCache<object> shallowClassToCache = new ClrCache<object>();
1919
private static readonly ConcurrentLazyCache<object> typeConvertCache = new ConcurrentLazyCache<object>();
20-
private static readonly ClrCache<object?> fieldCache = new ClrCache<object?>();
20+
private static readonly GenericClrCache<Tuple<Type, string>, object?> fieldCache = new GenericClrCache<Tuple<Type, string>, object?>();
2121
private static readonly ClrCache<Dictionary<string, Type>> ignoredEventInfoCache = new ClrCache<Dictionary<string, Type>>();
2222
private static readonly ClrCache<List<MemberInfo>> allMembersCache = new ClrCache<List<MemberInfo>>();
2323
private static readonly GenericClrCache<MemberInfo, bool> memberIgnoreStatusCache = new GenericClrCache<MemberInfo, bool>();
2424
private static readonly ClrCache<bool> typeContainsIgnoredMembersCache = new ClrCache<bool>();
2525
private static readonly ClrCache<object> specialTypesCache = new ClrCache<object>();
2626

27-
public static object? GetOrAddField(Type type, Func<Type, object?> valueFactory) => fieldCache.GetOrAdd(type, valueFactory);
27+
public static object? GetOrAddField(Type type, string name, Func<Type, object?> valueFactory) => fieldCache.GetOrAdd(new Tuple<Type, string>(type, name), k => valueFactory(k.Item1));
2828
public static object? GetOrAddClass(Type type, Func<Type, object?> valueFactory) => classCache.GetOrAdd(type, valueFactory);
2929
public static object? GetOrAddStructAsObject(Type type, Func<Type, object?> valueFactory) => structCache.GetOrAdd(type, valueFactory);
3030
public static object GetOrAddDeepClassTo(Type type, Func<Type, object> valueFactory) => deepClassToCache.GetOrAdd(type, valueFactory);

FastCloner/Code/FieldAccessorGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ internal static class FieldAccessorGenerator
77
{
88
internal static Action<object, object> GetFieldSetter(FieldInfo field)
99
{
10-
return (Action<object, object>)FastClonerCache.GetOrAddField(field.DeclaringType, _ => CreateFieldSetter(field));
10+
return (Action<object, object>)FastClonerCache.GetOrAddField(field.DeclaringType, field.Name, _ => CreateFieldSetter(field));
1111
}
1212

1313
private static Action<object, object> CreateFieldSetter(FieldInfo field)

0 commit comments

Comments
 (0)