Skip to content

Commit 5b20e09

Browse files
committed
respect [NonSerialized] when cloning
1 parent 88dfe9a commit 5b20e09

File tree

4 files changed

+35
-12
lines changed

4 files changed

+35
-12
lines changed

FastCloner.Tests/CopyToObjectTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ public void TaskCancelledExceptionCloningTest()
271271

272272
try
273273
{
274-
// Create a cancelled task that will throw TaskCancelledException
274+
// Create a canceled task that will throw TaskCancelledException
275275
cts.Cancel();
276276
Task.Delay(100, cts.Token).GetAwaiter().GetResult();
277277
}

FastCloner.Tests/SpecialCaseTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,14 @@ private class TestPropsWithIgnored
587587
[FastClonerIgnore]
588588
public string B { get; set; } = "My string";
589589
}
590+
591+
private class TestPropsWithNonSerialized
592+
{
593+
public int A { get; set; } = 10;
594+
595+
[FastClonerIgnore]
596+
public string B { get; set; } = "My string";
597+
}
590598

591599
[Test]
592600
public void Test_Clone_Props()
@@ -615,6 +623,20 @@ public void Test_Clone_Props_With_Ignored()
615623
Assert.That(clone, Is.Not.SameAs(original));
616624
});
617625
}
626+
627+
[Test]
628+
public void Test_Clone_Props_With_NonSerialized()
629+
{
630+
TestPropsWithNonSerialized original = new TestPropsWithNonSerialized { A = 42, B = "Test value" };
631+
TestPropsWithNonSerialized clone = original.DeepClone();
632+
633+
Assert.Multiple(() =>
634+
{
635+
Assert.That(clone.A, Is.EqualTo(42));
636+
Assert.That(clone.B, Is.EqualTo(null)); // default value
637+
Assert.That(clone, Is.Not.SameAs(original));
638+
});
639+
}
618640

619641
private class TestAutoProps
620642
{

FastCloner/Code/FastClonerExprGenerator.cs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ private static bool MemberIsIgnored(MemberInfo memberInfo)
3131
{
3232
return FastClonerCache.GetOrAddMemberIgnoreStatus(memberInfo, mi =>
3333
{
34-
FastClonerIgnoreAttribute? attribute = mi.GetCustomAttribute<FastClonerIgnoreAttribute>();
35-
return attribute?.Ignored ?? false;
34+
FastClonerIgnoreAttribute? fcIgnored = mi.GetCustomAttribute<FastClonerIgnoreAttribute>();
35+
NonSerializedAttribute? nonSerialized = mi.GetCustomAttribute<NonSerializedAttribute>();
36+
return fcIgnored?.Ignored ?? nonSerialized is not null;
3637
});
3738
}
3839

@@ -193,17 +194,17 @@ private static void AddMemberCloneExpressions(
193194
PropertyInfo pi => pi.PropertyType,
194195
_ => throw new ArgumentException($"Unsupported member type: {member.GetType()}")
195196
};
196-
197-
bool isWritable = member switch
197+
198+
bool canAssignDirect = member switch
198199
{
199200
FieldInfo fi => !fi.IsInitOnly,
200201
PropertyInfo pi => pi.CanWrite,
201202
_ => false
202-
} || type.IsClass() && member is FieldInfo { IsInitOnly: true };
203+
};
203204

204205
if (MemberIsIgnored(member))
205206
{
206-
if (isWritable)
207+
if (canAssignDirect)
207208
{
208209
expressionList.Add(Expression.Assign(
209210
Expression.MakeMemberAccess(toLocal, member),
@@ -218,7 +219,7 @@ private static void AddMemberCloneExpressions(
218219
{
219220
if (evtType == memberType)
220221
{
221-
if (isWritable)
222+
if (canAssignDirect)
222223
{
223224
expressionList.Add(Expression.Assign(
224225
Expression.MakeMemberAccess(toLocal, member),
@@ -232,7 +233,7 @@ private static void AddMemberCloneExpressions(
232233

233234
if (FastClonerCache.IsTypeIgnored(memberType))
234235
{
235-
if (isWritable)
236+
if (canAssignDirect)
236237
{
237238
expressionList.Add(Expression.Assign(
238239
Expression.MakeMemberAccess(toLocal, member),
@@ -245,7 +246,7 @@ private static void AddMemberCloneExpressions(
245246

246247
if (member is PropertyInfo piLocal)
247248
{
248-
if (isWritable && MemberIsIgnored(piLocal))
249+
if (piLocal.CanWrite && MemberIsIgnored(piLocal))
249250
{
250251
expressionList.Add(Expression.Assign(
251252
Expression.Property(toLocal, piLocal),
@@ -277,7 +278,7 @@ private static void AddMemberCloneExpressions(
277278

278279
if (shouldBeIgnored)
279280
{
280-
if (member is FieldInfo or PropertyInfo { CanWrite: true })
281+
if (canAssignDirect)
281282
{
282283
expressionList.Add(Expression.Assign(
283284
Expression.MakeMemberAccess(toLocal, member),

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Sometimes, you might want to exclude certain fields (including event synthesized
4141
```csharp
4242
private class TestPropsWithIgnored
4343
{
44-
[FastClonerIgnore] // <-- decorate with [FastClonerIgnore]
44+
[FastClonerIgnore] // <-- decorate with [FastClonerIgnore] or [NonSerialized]
4545
public string B { get; set; } = "My string";
4646

4747
public int A { get; set; } = 10;

0 commit comments

Comments
 (0)