Skip to content

Commit e526f64

Browse files
committed
add missing primitives, support unnamed types
1 parent b8e2090 commit e526f64

File tree

2 files changed

+159
-13
lines changed

2 files changed

+159
-13
lines changed

FastCloner.Tests/SpecialCaseTests.cs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,130 @@ public string Prop
940940
}
941941
}
942942

943+
private unsafe class UnnamedTypeContainer
944+
{
945+
public int Value;
946+
public object? Object;
947+
public delegate*<IServiceProvider, object> Builder;
948+
}
949+
950+
[Test]
951+
public unsafe void Test_Unnamed_Type()
952+
{
953+
// Arrange
954+
int[] array = [1, 2, 3];
955+
IntPtr builder = (IntPtr)GCHandle.Alloc(array, GCHandleType.Pinned);
956+
UnnamedTypeContainer obj = new UnnamedTypeContainer
957+
{
958+
Value = 1,
959+
Object = new object(),
960+
Builder = (delegate*<IServiceProvider, object>)builder
961+
};
962+
963+
// Act
964+
UnnamedTypeContainer result = obj.DeepClone();
965+
966+
// Assert
967+
Assert.Multiple(() =>
968+
{
969+
Assert.That(result, Is.Not.EqualTo(obj));
970+
Assert.That(result.Value, Is.EqualTo(obj.Value));
971+
Assert.That(result.Object, Is.Not.EqualTo(obj.Object));
972+
Assert.That(result.Builder == obj.Builder, Is.True);
973+
});
974+
}
975+
976+
[Test]
977+
public void Test_TimeSpan()
978+
{
979+
// Arrange
980+
TimeSpan obj = TimeSpan.FromHours(42.5);
981+
982+
// Act
983+
TimeSpan result = obj.DeepClone();
984+
985+
// Assert
986+
Assert.That(result, Is.EqualTo(obj));
987+
}
988+
989+
[Test]
990+
public void Test_TimeZoneInfo()
991+
{
992+
// Arrange
993+
TimeZoneInfo obj = TimeZoneInfo.Local;
994+
995+
// Act
996+
TimeZoneInfo result = obj.DeepClone();
997+
998+
// Assert
999+
Assert.That(result, Is.EqualTo(obj));
1000+
}
1001+
1002+
[Test]
1003+
public void Test_Half()
1004+
{
1005+
// Arrange
1006+
Half obj = (Half)42.5f;
1007+
1008+
// Act
1009+
Half result = obj.DeepClone();
1010+
1011+
// Assert
1012+
Assert.That(result, Is.EqualTo(obj));
1013+
}
1014+
1015+
[Test]
1016+
public void Test_Int128()
1017+
{
1018+
// Arrange
1019+
Int128 obj = Int128.Parse("123456789012345678901234567890");
1020+
1021+
// Act
1022+
Int128 result = obj.DeepClone();
1023+
1024+
// Assert
1025+
Assert.That(result, Is.EqualTo(obj));
1026+
}
1027+
1028+
[Test]
1029+
public void Test_UInt128()
1030+
{
1031+
// Arrange
1032+
UInt128 obj = UInt128.Parse("123456789012345678901234567890");
1033+
1034+
// Act
1035+
UInt128 result = obj.DeepClone();
1036+
1037+
// Assert
1038+
Assert.That(result, Is.EqualTo(obj));
1039+
}
1040+
1041+
[Test]
1042+
public void Test_Char()
1043+
{
1044+
// Arrange
1045+
char obj = 'Ž';
1046+
1047+
// Act
1048+
char result = obj.DeepClone();
1049+
1050+
// Assert
1051+
Assert.That(result, Is.EqualTo(obj));
1052+
}
1053+
1054+
[Test]
1055+
public void Test_Bool()
1056+
{
1057+
// Arrange
1058+
bool obj = true;
1059+
1060+
// Act
1061+
bool result = obj.DeepClone();
1062+
1063+
// Assert
1064+
Assert.That(result, Is.EqualTo(obj));
1065+
}
1066+
9431067
[Test]
9441068
public void Test_Notify_Triggered_Correctly()
9451069
{

FastCloner/Code/FastClonerSafeTypes.cs

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,44 +22,60 @@ internal static class FastClonerSafeTypes
2222
[typeof(double)] = true,
2323
[typeof(decimal)] = true,
2424
[typeof(string)] = true,
25+
[typeof(char)] = true,
26+
[typeof(bool)] = true,
27+
[typeof(sbyte)] = true,
28+
[typeof(nint)] = true,
29+
[typeof(nuint)] = true,
30+
[typeof(Guid)] = true,
31+
32+
// Time-related types
33+
[typeof(TimeSpan)] = true,
34+
[typeof(TimeZoneInfo)] = true,
2535
[typeof(DateTime)] = true,
2636
[typeof(DateTimeOffset)] = true,
2737
[typeof(DateOnly)] = true,
2838
[typeof(TimeOnly)] = true,
29-
[typeof(IntPtr)] = true,
30-
[typeof(UIntPtr)] = true,
31-
[typeof(Guid)] = true,
39+
40+
// Numeric types
41+
[typeof(Half)] = true,
42+
[typeof(Int128)] = true,
43+
[typeof(UInt128)] = true,
3244

3345
// Others
3446
[typeof(DBNull)] = true,
3547
[StringComparer.Ordinal.GetType()] = true,
3648
[StringComparer.OrdinalIgnoreCase.GetType()] = true,
49+
[StringComparer.InvariantCulture.GetType()] = true,
50+
[StringComparer.InvariantCultureIgnoreCase.GetType()] = true
3751
};
3852

3953
static FastClonerSafeTypes()
4054
{
41-
foreach (
42-
Type? x in
43-
new[]
44-
{
45-
Type.GetType("System.RuntimeType"),
46-
Type.GetType("System.RuntimeTypeHandle"),
47-
StringComparer.InvariantCulture.GetType(),
48-
StringComparer.InvariantCultureIgnoreCase.GetType(),
49-
}) KnownTypes.TryAdd(x, true);
55+
List<Type?> safeTypes =
56+
[
57+
Type.GetType("System.RuntimeType"),
58+
Type.GetType("System.RuntimeTypeHandle")
59+
];
60+
61+
foreach (Type? x in safeTypes.OfType<Type>())
62+
{
63+
KnownTypes.TryAdd(x, true);
64+
}
5065
}
5166

5267
private static bool CanReturnSameType(Type type, HashSet<Type>? processingTypes)
5368
{
5469
if (KnownTypes.TryGetValue(type, out bool isSafe))
70+
{
5571
return isSafe;
72+
}
5673

5774
if (typeof(Delegate).IsAssignableFrom(type))
5875
{
5976
KnownTypes.TryAdd(type, false);
6077
return false;
6178
}
62-
6379

6480
// enums are safe
6581
// pointers (e.g. int*) are unsafe, but we cannot do anything with it except blind copy
@@ -68,6 +84,12 @@ private static bool CanReturnSameType(Type type, HashSet<Type>? processingTypes)
6884
KnownTypes.TryAdd(type, true);
6985
return true;
7086
}
87+
88+
if (type.FullName is null)
89+
{
90+
KnownTypes.TryAdd(type, true);
91+
return true;
92+
}
7193

7294
if (type.FullName.StartsWith("System.Reflection.") && type.Assembly == typeof(PropertyInfo).Assembly)
7395
{

0 commit comments

Comments
 (0)