11using System . Collections ;
22using System . Collections . Concurrent ;
3+ #if NET8_0_OR_GREATER
34using System . Collections . Frozen ;
5+ #endif
46using System . Collections . ObjectModel ;
57using System . Dynamic ;
68using System . Linq . Expressions ;
@@ -13,9 +15,14 @@ internal static class FastClonerExprGenerator
1315 internal static readonly ConcurrentDictionary < Type , Func < Type , bool , ExpressionPosition , object > > CustomTypeHandlers = [ ] ;
1416 private static readonly ConcurrentDictionary < FieldInfo , bool > readonlyFields = new ConcurrentDictionary < FieldInfo , bool > ( ) ;
1517 private static readonly MethodInfo fieldSetMethod ;
16- private static readonly Lazy < MethodInfo > _isTypeIgnoredMethodInfo = new Lazy < MethodInfo > ( ( ) => typeof ( FastClonerCache ) . GetMethod ( nameof ( FastClonerCache . IsTypeIgnored ) , BindingFlags . NonPublic | BindingFlags . Public | BindingFlags . Static , [ typeof ( Type ) ] ) ! , LazyThreadSafetyMode . ExecutionAndPublication ) ;
18+
19+ #if MODERN
20+ private static readonly Lazy < MethodInfo > isTypeIgnoredMethodInfo = new Lazy < MethodInfo > ( ( ) => typeof ( FastClonerCache ) . GetMethod ( nameof ( FastClonerCache . IsTypeIgnored ) , BindingFlags . NonPublic | BindingFlags . Public | BindingFlags . Static , [ typeof ( Type ) ] ) ! , LazyThreadSafetyMode . ExecutionAndPublication ) ;
21+ #else
22+ private static readonly Lazy < MethodInfo > isTypeIgnoredMethodInfo = new Lazy < MethodInfo > ( ( ) => typeof ( FastClonerCache ) . GetMethod ( nameof ( FastClonerCache . IsTypeIgnored ) , BindingFlags . NonPublic | BindingFlags . Public | BindingFlags . Static , null , new Type [ ] { typeof ( Type ) } , null ) ! , LazyThreadSafetyMode . ExecutionAndPublication ) ;
23+ #endif
1724
18- internal static MethodInfo IsTypeIgnoredMethodInfo => _isTypeIgnoredMethodInfo . Value ;
25+ internal static MethodInfo IsTypeIgnoredMethodInfo => isTypeIgnoredMethodInfo . Value ;
1926
2027 static FastClonerExprGenerator ( )
2128 {
@@ -44,11 +51,61 @@ internal static void ForceSetField(FieldInfo field, object obj, object value)
4451 field . SetValue ( obj , value ) ;
4552 }
4653
54+ #if MODERN
4755 internal readonly record struct ExpressionPosition ( int Depth , int Index )
4856 {
4957 public ExpressionPosition Next ( ) => this with { Index = Index + 1 } ;
5058 public ExpressionPosition Nested ( ) => new ExpressionPosition ( Depth + 1 , 0 ) ;
5159 }
60+ #else
61+ internal readonly struct ExpressionPosition : IEquatable < ExpressionPosition >
62+ {
63+ public int Depth { get ; }
64+ public int Index { get ; }
65+
66+ public ExpressionPosition ( int depth , int index )
67+ {
68+ Depth = depth ;
69+ Index = index ;
70+ }
71+
72+ public ExpressionPosition Next ( ) => new ExpressionPosition ( Depth , Index + 1 ) ;
73+ public ExpressionPosition Nested ( ) => new ExpressionPosition ( Depth + 1 , 0 ) ;
74+
75+ public bool Equals ( ExpressionPosition other )
76+ {
77+ return Depth == other . Depth && Index == other . Index ;
78+ }
79+
80+ public override bool Equals ( object obj )
81+ {
82+ return obj is ExpressionPosition other && Equals ( other ) ;
83+ }
84+
85+ public override int GetHashCode ( )
86+ {
87+ unchecked
88+ {
89+ return ( Depth * 397 ) ^ Index ;
90+ }
91+ }
92+
93+ public static bool operator == ( ExpressionPosition left , ExpressionPosition right )
94+ {
95+ return left . Equals ( right ) ;
96+ }
97+
98+ public static bool operator != ( ExpressionPosition left , ExpressionPosition right )
99+ {
100+ return ! left . Equals ( right ) ;
101+ }
102+
103+ public override string ToString ( )
104+ {
105+ return $ "ExpressionPosition {{ Depth = { Depth } , Index = { Index } }}";
106+ }
107+ }
108+ #endif
52109
53110 private static LabelTarget CreateLoopLabel ( ExpressionPosition position )
54111 {
@@ -62,13 +119,22 @@ private static LabelTarget CreateLoopLabel(ExpressionPosition position)
62119
63120 private delegate object ProcessMethodDelegate ( Type type , bool unboxStruct , ExpressionPosition position ) ;
64121
122+ #if MODERN
65123 private static readonly FrozenDictionary < Type , ProcessMethodDelegate > knownTypeProcessors =
66124 new Dictionary < Type , ProcessMethodDelegate >
67125 {
68126 [ typeof ( ExpandoObject ) ] = ( _ , _ , position ) => GenerateExpandoObjectProcessor ( position ) ,
69127 [ typeof ( HttpRequestOptions ) ] = ( _ , _ , position ) => GenerateHttpRequestOptionsProcessor ( position ) ,
70128 [ typeof ( Array ) ] = ( type , _ , _ ) => GenerateProcessArrayMethod ( type ) ,
71129 } . ToFrozenDictionary ( ) ;
130+ #else
131+ private static readonly Dictionary < Type , ProcessMethodDelegate > knownTypeProcessors =
132+ new Dictionary < Type , ProcessMethodDelegate >
133+ {
134+ [ typeof ( ExpandoObject ) ] = ( _ , _ , position ) => GenerateExpandoObjectProcessor ( position ) ,
135+ [ typeof ( Array ) ] = ( type , _ , _ ) => GenerateProcessArrayMethod ( type ) ,
136+ } ;
137+ #endif
72138
73139 private static readonly AhoCorasick badTypes = new AhoCorasick ( [
74140 "Castle.Proxies." ,
@@ -385,6 +451,7 @@ private static List<MemberInfo> GetAllMembers(Type type)
385451 return Expression . Lambda ( funcType , Expression . Block ( blockParams , expressionList ) , from , state ) . Compile ( ) ;
386452 }
387453
454+ #if MODERN
388455 private static object GenerateHttpRequestOptionsProcessor ( ExpressionPosition position )
389456 {
390457 if ( FastClonerCache . IsTypeIgnored ( typeof ( HttpRequestOptions ) ) )
@@ -418,6 +485,7 @@ private static object GenerateHttpRequestOptionsProcessor(ExpressionPosition pos
418485
419486 return Expression . Lambda < Func < object , FastCloneState , object > > ( block , from , state ) . Compile ( ) ;
420487 }
488+ #endif
421489
422490 private static object GenerateExpandoObjectProcessor ( ExpressionPosition position )
423491 {
@@ -554,8 +622,13 @@ private static object GenerateDictionaryProcessor(Type dictType, Type keyType, T
554622 }
555623
556624 // For read-only collections
625+ #if MODERN
557626 bool isReadOnly = dictType . Name . Contains ( "ReadOnly" , StringComparison . InvariantCultureIgnoreCase ) ||
558627 ( dictType . IsGenericType && dictType . GetGenericTypeDefinition ( ) == typeof ( ReadOnlyDictionary < , > ) ) ;
628+ #else
629+ bool isReadOnly = dictType . Name . IndexOf ( "ReadOnly" , StringComparison . InvariantCultureIgnoreCase ) >= 0 ||
630+ ( dictType . IsGenericType && dictType . GetGenericTypeDefinition ( ) == typeof ( ReadOnlyDictionary < , > ) ) ;
631+ #endif
559632
560633 Type innerDictType = isReadOnly
561634 ? typeof ( Dictionary < , > ) . MakeGenericType ( keyType , valueType )
@@ -1082,7 +1155,11 @@ private static object GenerateProcessSetMethod(Type type, ExpressionPosition pos
10821155
10831156 ParameterExpression local = Expression . Variable ( type ) ;
10841157
1158+ #if MODERN
10851159 bool isReadOnly = type . Name . Contains ( "ReadOnly" , StringComparison . InvariantCultureIgnoreCase ) ;
1160+ #else
1161+ bool isReadOnly = type . Name . IndexOf ( "ReadOnly" , StringComparison . InvariantCultureIgnoreCase ) >= 0 ;
1162+ #endif
10861163
10871164 // Use HashSet as inner collection
10881165 Type innerSetType = isReadOnly
0 commit comments