11#if ! ( UAP10_0 )
2+ using System . Collections ;
23using System . Collections . Concurrent ;
34using System . Collections . Generic ;
45using System . Diagnostics ;
@@ -21,26 +22,23 @@ namespace System.Linq.Dynamic.Core
2122 /// </summary>
2223 public static class DynamicClassFactory
2324 {
24- // EmptyTypes is used to indicate that we are looking for someting without any parameters.
25- private static readonly Type [ ] EmptyTypes = new Type [ 0 ] ;
26-
2725 private static readonly ConcurrentDictionary < string , Type > GeneratedTypes = new ConcurrentDictionary < string , Type > ( ) ;
2826
2927 private static readonly ModuleBuilder ModuleBuilder ;
3028
3129 // Some objects we cache
32- private static readonly CustomAttributeBuilder CompilerGeneratedAttributeBuilder = new CustomAttributeBuilder ( typeof ( CompilerGeneratedAttribute ) . GetConstructor ( EmptyTypes ) , new object [ 0 ] ) ;
30+ private static readonly CustomAttributeBuilder CompilerGeneratedAttributeBuilder = new CustomAttributeBuilder ( typeof ( CompilerGeneratedAttribute ) . GetConstructor ( Type . EmptyTypes ) , new object [ 0 ] ) ;
3331 private static readonly CustomAttributeBuilder DebuggerBrowsableAttributeBuilder = new CustomAttributeBuilder ( typeof ( DebuggerBrowsableAttribute ) . GetConstructor ( new [ ] { typeof ( DebuggerBrowsableState ) } ) , new object [ ] { DebuggerBrowsableState . Never } ) ;
34- private static readonly CustomAttributeBuilder DebuggerHiddenAttributeBuilder = new CustomAttributeBuilder ( typeof ( DebuggerHiddenAttribute ) . GetConstructor ( EmptyTypes ) , new object [ 0 ] ) ;
32+ private static readonly CustomAttributeBuilder DebuggerHiddenAttributeBuilder = new CustomAttributeBuilder ( typeof ( DebuggerHiddenAttribute ) . GetConstructor ( Type . EmptyTypes ) , new object [ 0 ] ) ;
3533
36- private static readonly ConstructorInfo ObjectCtor = typeof ( object ) . GetConstructor ( EmptyTypes ) ;
34+ private static readonly ConstructorInfo ObjectCtor = typeof ( object ) . GetConstructor ( Type . EmptyTypes ) ;
3735#if WINDOWS_APP || UAP10_0 || NETSTANDARD
3836 private static readonly MethodInfo ObjectToString = typeof ( object ) . GetMethod ( "ToString" , BindingFlags . Instance | BindingFlags . Public ) ;
3937#else
40- private static readonly MethodInfo ObjectToString = typeof ( object ) . GetMethod ( "ToString" , BindingFlags . Instance | BindingFlags . Public , null , EmptyTypes , null ) ;
38+ private static readonly MethodInfo ObjectToString = typeof ( object ) . GetMethod ( "ToString" , BindingFlags . Instance | BindingFlags . Public , null , Type . EmptyTypes , null ) ;
4139#endif
4240
43- private static readonly ConstructorInfo StringBuilderCtor = typeof ( StringBuilder ) . GetConstructor ( EmptyTypes ) ;
41+ private static readonly ConstructorInfo StringBuilderCtor = typeof ( StringBuilder ) . GetConstructor ( Type . EmptyTypes ) ;
4442#if WINDOWS_APP || UAP10_0 || NETSTANDARD
4543 private static readonly MethodInfo StringBuilderAppendString = typeof ( StringBuilder ) . GetMethod ( "Append" , new [ ] { typeof ( string ) } ) ;
4644 private static readonly MethodInfo StringBuilderAppendObject = typeof ( StringBuilder ) . GetMethod ( "Append" , new [ ] { typeof ( object ) } ) ;
@@ -56,7 +54,7 @@ public static class DynamicClassFactory
5654 private static readonly MethodInfo EqualityComparerEquals = EqualityComparer . GetMethod ( "Equals" , new [ ] { EqualityComparerGenericArgument , EqualityComparerGenericArgument } ) ;
5755 private static readonly MethodInfo EqualityComparerGetHashCode = EqualityComparer . GetMethod ( "GetHashCode" , new [ ] { EqualityComparerGenericArgument } ) ;
5856#else
59- private static readonly MethodInfo EqualityComparerDefault = EqualityComparer . GetMethod ( "get_Default" , BindingFlags . Static | BindingFlags . Public , null , EmptyTypes , null ) ;
57+ private static readonly MethodInfo EqualityComparerDefault = EqualityComparer . GetMethod ( "get_Default" , BindingFlags . Static | BindingFlags . Public , null , Type . EmptyTypes , null ) ;
6058 private static readonly MethodInfo EqualityComparerEquals = EqualityComparer . GetMethod ( "Equals" , BindingFlags . Instance | BindingFlags . Public , null , new [ ] { EqualityComparerGenericArgument , EqualityComparerGenericArgument } , null ) ;
6159 private static readonly MethodInfo EqualityComparerGetHashCode = EqualityComparer . GetMethod ( "GetHashCode" , BindingFlags . Instance | BindingFlags . Public , null , new [ ] { EqualityComparerGenericArgument } , null ) ;
6260#endif
@@ -85,6 +83,71 @@ static DynamicClassFactory()
8583 ModuleBuilder = assemblyBuilder . DefineDynamicModule ( DynamicModuleName ) ;
8684 }
8785
86+ /// <summary>
87+ /// Create a GenericComparerType based on the GenericType and an instance of a <see cref="IComparer"/>.
88+ /// </summary>
89+ /// <param name="comparerGenericType">The GenericType</param>
90+ /// <param name="comparerType">The <see cref="IComparer"/> instance</param>
91+ /// <returns>Type</returns>
92+ public static Type CreateGenericComparerType ( Type comparerGenericType , Type comparerType )
93+ {
94+ Check . NotNull ( comparerGenericType , nameof ( comparerGenericType ) ) ;
95+ Check . NotNull ( comparerType , nameof ( comparerType ) ) ;
96+
97+ var key = $ "{ comparerGenericType . FullName } _{ comparerType . FullName } ";
98+
99+ if ( ! GeneratedTypes . TryGetValue ( key , out var type ) )
100+ {
101+ // We create only a single class at a time, through this lock
102+ // Note that this is a variant of the double-checked locking.
103+ // It is safe because we are using a thread safe class.
104+ lock ( GeneratedTypes )
105+ {
106+ if ( ! GeneratedTypes . TryGetValue ( key , out type ) )
107+ {
108+ var compareMethodGeneric = comparerGenericType . GetMethod ( "Compare" ) ;
109+ var compareMethod = typeof ( IComparer ) . GetMethod ( "Compare" ) ;
110+ var compareCtor = comparerType . GetConstructor ( Type . EmptyTypes ) ;
111+
112+ var typeBuilder = ModuleBuilder . DefineType ( key , TypeAttributes . AnsiClass | TypeAttributes . Public | TypeAttributes . Class | TypeAttributes . AutoLayout , typeof ( object ) ) ;
113+ typeBuilder . AddInterfaceImplementation ( comparerGenericType ) ;
114+
115+ var fieldBuilder = typeBuilder . DefineField ( "_c" , typeof ( IComparer ) , FieldAttributes . Private | FieldAttributes . InitOnly ) ;
116+
117+ var constructorBuilder = typeBuilder . DefineConstructor ( MethodAttributes . Public | MethodAttributes . HideBySig , CallingConventions . HasThis , Type . EmptyTypes ) ;
118+ var constructorIL = constructorBuilder . GetILGenerator ( ) ;
119+ constructorIL . Emit ( OpCodes . Ldarg_0 ) ;
120+ constructorIL . Emit ( OpCodes . Call , ObjectCtor ) ;
121+ constructorIL . Emit ( OpCodes . Ldarg_0 ) ;
122+ constructorIL . Emit ( OpCodes . Newobj , compareCtor ) ;
123+ constructorIL . Emit ( OpCodes . Stfld , fieldBuilder ) ;
124+ constructorIL . Emit ( OpCodes . Ret ) ;
125+
126+ var methodBuilder = typeBuilder . DefineMethod (
127+ compareMethodGeneric . Name ,
128+ compareMethodGeneric . Attributes & ~ MethodAttributes . Abstract ,
129+ compareMethodGeneric . CallingConvention ,
130+ compareMethodGeneric . ReturnType ,
131+ compareMethodGeneric . GetParameters ( ) . Select ( p => p . ParameterType ) . ToArray ( )
132+ ) ;
133+ var methodBuilderIL = methodBuilder . GetILGenerator ( ) ;
134+ methodBuilderIL . Emit ( OpCodes . Ldarg_0 ) ;
135+ methodBuilderIL . Emit ( OpCodes . Ldfld , fieldBuilder ) ;
136+ methodBuilderIL . Emit ( OpCodes . Ldarg_1 ) ;
137+ methodBuilderIL . Emit ( OpCodes . Box , typeof ( int ) ) ;
138+ methodBuilderIL . Emit ( OpCodes . Ldarg_2 ) ;
139+ methodBuilderIL . Emit ( OpCodes . Box , typeof ( int ) ) ;
140+ methodBuilderIL . Emit ( OpCodes . Callvirt , compareMethod ) ;
141+ methodBuilderIL . Emit ( OpCodes . Ret ) ;
142+
143+ return GeneratedTypes . GetOrAdd ( key , typeBuilder . CreateType ( ) ) ;
144+ }
145+ }
146+ }
147+
148+ return type ;
149+ }
150+
88151 /// <summary>
89152 /// The CreateType method creates a new data class with a given set of public properties and returns the System.Type object for the newly created class. If a data class with an identical sequence of properties has already been created, the System.Type object for this class is returned.
90153 /// Data classes implement private instance variables and read/write property accessors for the specified properties.Data classes also override the Equals and GetHashCode members to implement by-value equality.
@@ -157,7 +220,7 @@ public static Type CreateType([NotNull] IList<DynamicProperty> properties, bool
157220 fields [ i ] = tb . DefineField ( $ "<{ names [ i ] } >i__Field", generics [ i ] . AsType ( ) , FieldAttributes . Private | FieldAttributes . InitOnly ) ;
158221 fields [ i ] . SetCustomAttribute ( DebuggerBrowsableAttributeBuilder ) ;
159222
160- PropertyBuilder property = tb . DefineProperty ( names [ i ] , PropertyAttributes . None , CallingConventions . HasThis , generics [ i ] . AsType ( ) , EmptyTypes ) ;
223+ PropertyBuilder property = tb . DefineProperty ( names [ i ] , PropertyAttributes . None , CallingConventions . HasThis , generics [ i ] . AsType ( ) , Type . EmptyTypes ) ;
161224
162225 // getter
163226 MethodBuilder getter = tb . DefineMethod ( $ "get_{ names [ i ] } ", MethodAttributes . Public | MethodAttributes . HideBySig | MethodAttributes . SpecialName , CallingConventions . HasThis , generics [ i ] . AsType ( ) , null ) ;
@@ -184,7 +247,7 @@ public static Type CreateType([NotNull] IList<DynamicProperty> properties, bool
184247 }
185248
186249 // ToString()
187- MethodBuilder toString = tb . DefineMethod ( "ToString" , MethodAttributes . Public | MethodAttributes . Virtual | MethodAttributes . HideBySig , CallingConventions . HasThis , typeof ( string ) , EmptyTypes ) ;
250+ MethodBuilder toString = tb . DefineMethod ( "ToString" , MethodAttributes . Public | MethodAttributes . Virtual | MethodAttributes . HideBySig , CallingConventions . HasThis , typeof ( string ) , Type . EmptyTypes ) ;
188251 toString . SetCustomAttribute ( DebuggerHiddenAttributeBuilder ) ;
189252 ILGenerator ilgeneratorToString = toString . GetILGenerator ( ) ;
190253 ilgeneratorToString . DeclareLocal ( typeof ( StringBuilder ) ) ;
@@ -206,7 +269,7 @@ public static Type CreateType([NotNull] IList<DynamicProperty> properties, bool
206269 Label equalsLabel = ilgeneratorEquals . DefineLabel ( ) ;
207270
208271 // GetHashCode()
209- MethodBuilder getHashCode = tb . DefineMethod ( "GetHashCode" , MethodAttributes . Public | MethodAttributes . Virtual | MethodAttributes . HideBySig , CallingConventions . HasThis , typeof ( int ) , EmptyTypes ) ;
272+ MethodBuilder getHashCode = tb . DefineMethod ( "GetHashCode" , MethodAttributes . Public | MethodAttributes . Virtual | MethodAttributes . HideBySig , CallingConventions . HasThis , typeof ( int ) , Type . EmptyTypes ) ;
210273 getHashCode . SetCustomAttribute ( DebuggerHiddenAttributeBuilder ) ;
211274 ILGenerator ilgeneratorGetHashCode = getHashCode . GetILGenerator ( ) ;
212275 ilgeneratorGetHashCode . DeclareLocal ( typeof ( int ) ) ;
@@ -283,7 +346,7 @@ public static Type CreateType([NotNull] IList<DynamicProperty> properties, bool
283346 if ( createParameterCtor && names . Any ( ) )
284347 {
285348 // .ctor default
286- ConstructorBuilder constructorDef = tb . DefineConstructor ( MethodAttributes . Public | MethodAttributes . HideBySig , CallingConventions . HasThis , EmptyTypes ) ;
349+ ConstructorBuilder constructorDef = tb . DefineConstructor ( MethodAttributes . Public | MethodAttributes . HideBySig , CallingConventions . HasThis , Type . EmptyTypes ) ;
287350 constructorDef . SetCustomAttribute ( DebuggerHiddenAttributeBuilder ) ;
288351
289352 ILGenerator ilgeneratorConstructorDef = constructorDef . GetILGenerator ( ) ;
0 commit comments