1
1
// Licensed to the .NET Foundation under one or more agreements.
2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
- using System ;
5
4
using System . Collections . Concurrent ;
6
5
using System . Diagnostics ;
7
6
using System . Diagnostics . CodeAnalysis ;
8
7
using System . Globalization ;
9
- using System . Linq ;
10
8
using System . Linq . Expressions ;
11
9
using System . Numerics ;
12
10
using System . Reflection ;
11
+ using System . Runtime . CompilerServices ;
13
12
14
13
#nullable enable
15
14
@@ -24,7 +23,9 @@ internal sealed class TryParseMethodCache
24
23
25
24
internal readonly ParameterExpression TempSourceStringExpr = Expression . Variable ( typeof ( string ) , "tempSourceString" ) ;
26
25
27
- public TryParseMethodCache ( ) : this ( preferNonGenericEnumParseOverload : false )
26
+ // If IsDynamicCodeSupported is false, we can't use the static Enum.TryParse<T> since there's no easy way for
27
+ // this code to generate the specific instantiation for any enums used
28
+ public TryParseMethodCache ( ) : this ( preferNonGenericEnumParseOverload : ! RuntimeFeature . IsDynamicCodeSupported )
28
29
{
29
30
}
30
31
@@ -121,146 +122,181 @@ public bool HasTryParseMethod(ParameterInfo parameter)
121
122
122
123
private static MethodInfo GetEnumTryParseMethod ( bool preferNonGenericEnumParseOverload )
123
124
{
124
- var staticEnumMethods = typeof ( Enum ) . GetMethods ( BindingFlags . Public | BindingFlags . Static ) ;
125
+ MethodInfo ? methodInfo = null ;
125
126
126
- // With NativeAOT, if there's no static usage of Enum.TryParse<T>, it will be removed
127
- // we fallback to the non-generic version if that is the case
128
- MethodInfo ? genericCandidate = null ;
129
- MethodInfo ? nonGenericCandidate = null ;
130
-
131
- foreach ( var method in staticEnumMethods )
127
+ if ( preferNonGenericEnumParseOverload )
132
128
{
133
- if ( method . Name != nameof ( Enum . TryParse ) || method . ReturnType != typeof ( bool ) )
134
- {
135
- continue ;
136
- }
137
-
138
- var tryParseParameters = method . GetParameters ( ) ;
139
-
140
- // Enum.TryParse<T>(string, out object)
141
- if ( method . IsGenericMethod &&
142
- tryParseParameters . Length == 2 &&
143
- tryParseParameters [ 0 ] . ParameterType == typeof ( string ) &&
144
- tryParseParameters [ 1 ] . IsOut )
145
- {
146
- genericCandidate = method ;
147
- }
148
-
149
- // Enum.TryParse(type, string, out object)
150
- if ( ! method . IsGenericMethod &&
151
- tryParseParameters . Length == 3 &&
152
- tryParseParameters [ 0 ] . ParameterType == typeof ( Type ) &&
153
- tryParseParameters [ 1 ] . ParameterType == typeof ( string ) &&
154
- tryParseParameters [ 2 ] . IsOut )
155
- {
156
- nonGenericCandidate = method ;
157
- }
129
+ methodInfo = typeof ( Enum ) . GetMethod (
130
+ nameof ( Enum . TryParse ) ,
131
+ BindingFlags . Public | BindingFlags . Static ,
132
+ new [ ] { typeof ( Type ) , typeof ( string ) , typeof ( object ) . MakeByRefType ( ) } ) ;
158
133
}
159
-
160
- if ( genericCandidate is null && nonGenericCandidate is null )
134
+ else
161
135
{
162
- Debug . Fail ( "No suitable System.Enum.TryParse method found." ) ;
163
- throw new MissingMethodException ( "No suitable System.Enum.TryParse method found." ) ;
136
+ methodInfo = typeof ( Enum ) . GetMethod (
137
+ nameof ( Enum . TryParse ) ,
138
+ genericParameterCount : 1 ,
139
+ new [ ] { typeof ( string ) , Type . MakeGenericMethodParameter ( 0 ) . MakeByRefType ( ) } ) ;
164
140
}
165
141
166
- if ( preferNonGenericEnumParseOverload )
142
+ if ( methodInfo is null )
167
143
{
168
- return nonGenericCandidate ! ;
144
+ Debug . Fail ( "No suitable System.Enum.TryParse method found." ) ;
145
+ throw new MissingMethodException ( "No suitable System.Enum.TryParse method found." ) ;
169
146
}
170
147
171
- return genericCandidate ?? nonGenericCandidate ! ;
148
+ return methodInfo ! ;
172
149
}
173
150
174
151
private static bool TryGetDateTimeTryParseMethod ( Type type , [ NotNullWhen ( true ) ] out MethodInfo ? methodInfo )
175
152
{
176
153
methodInfo = null ;
177
- if ( type != typeof ( DateTime ) && type != typeof ( DateOnly ) &&
178
- type != typeof ( DateTimeOffset ) && type != typeof ( TimeOnly ) )
154
+
155
+ if ( type == typeof ( DateTime ) )
179
156
{
180
- return false ;
157
+ methodInfo = typeof ( DateTime ) . GetMethod (
158
+ nameof ( DateTime . TryParse ) ,
159
+ BindingFlags . Public | BindingFlags . Static ,
160
+ new [ ] { typeof ( string ) , typeof ( IFormatProvider ) , typeof ( DateTimeStyles ) , typeof ( DateTime ) . MakeByRefType ( ) } ) ;
161
+ }
162
+ else if ( type == typeof ( DateTimeOffset ) )
163
+ {
164
+ methodInfo = typeof ( DateTimeOffset ) . GetMethod (
165
+ nameof ( DateTimeOffset . TryParse ) ,
166
+ BindingFlags . Public | BindingFlags . Static ,
167
+ new [ ] { typeof ( string ) , typeof ( IFormatProvider ) , typeof ( DateTimeStyles ) , typeof ( DateTimeOffset ) . MakeByRefType ( ) } ) ;
168
+ }
169
+ else if ( type == typeof ( DateOnly ) )
170
+ {
171
+ methodInfo = typeof ( DateOnly ) . GetMethod (
172
+ nameof ( DateOnly . TryParse ) ,
173
+ BindingFlags . Public | BindingFlags . Static ,
174
+ new [ ] { typeof ( string ) , typeof ( IFormatProvider ) , typeof ( DateTimeStyles ) , typeof ( DateOnly ) . MakeByRefType ( ) } ) ;
175
+ }
176
+ else if ( type == typeof ( TimeOnly ) )
177
+ {
178
+ methodInfo = typeof ( TimeOnly ) . GetMethod (
179
+ nameof ( TimeOnly . TryParse ) ,
180
+ BindingFlags . Public | BindingFlags . Static ,
181
+ new [ ] { typeof ( string ) , typeof ( IFormatProvider ) , typeof ( DateTimeStyles ) , typeof ( TimeOnly ) . MakeByRefType ( ) } ) ;
181
182
}
182
-
183
- var staticTryParseDateMethod = type . GetMethod (
184
- "TryParse" ,
185
- BindingFlags . Public | BindingFlags . Static ,
186
- new [ ] { typeof ( string ) , typeof ( IFormatProvider ) , typeof ( DateTimeStyles ) , type . MakeByRefType ( ) } ) ;
187
-
188
- methodInfo = staticTryParseDateMethod ;
189
183
190
184
return methodInfo != null ;
191
185
}
192
186
193
187
private static bool TryGetNumberStylesTryGetMethod ( Type type , [ NotNullWhen ( true ) ] out MethodInfo ? method , [ NotNullWhen ( true ) ] out NumberStyles ? numberStyles )
194
188
{
195
189
method = null ;
196
- numberStyles = null ;
190
+ numberStyles = NumberStyles . Integer ;
197
191
198
- if ( ! UseTryParseWithNumberStyleOption ( type ) )
192
+ if ( type == typeof ( long ) )
199
193
{
200
- return false ;
194
+ method = typeof ( long ) . GetMethod (
195
+ nameof ( long . TryParse ) ,
196
+ BindingFlags . Public | BindingFlags . Static ,
197
+ new [ ] { typeof ( string ) , typeof ( NumberStyles ) , typeof ( IFormatProvider ) , typeof ( long ) . MakeByRefType ( ) } ) ;
201
198
}
202
-
203
- var staticMethods = type . GetMethods ( BindingFlags . Public | BindingFlags . Static )
204
- . Where ( m => m . Name == "TryParse" && m . ReturnType == typeof ( bool ) )
205
- . OrderByDescending ( m => m . GetParameters ( ) . Length ) ;
206
-
207
- var numberStylesToUse = NumberStyles . Integer ;
208
- var methodToUse = default ( MethodInfo ) ;
209
-
210
- foreach ( var methodInfo in staticMethods )
199
+ else if ( type == typeof ( ulong ) )
211
200
{
212
- var tryParseParameters = methodInfo . GetParameters ( ) ;
213
-
214
- if ( tryParseParameters . Length == 4 &&
215
- tryParseParameters [ 0 ] . ParameterType == typeof ( string ) &&
216
- tryParseParameters [ 1 ] . ParameterType == typeof ( NumberStyles ) &&
217
- tryParseParameters [ 2 ] . ParameterType == typeof ( IFormatProvider ) &&
218
- tryParseParameters [ 3 ] . IsOut &&
219
- tryParseParameters [ 3 ] . ParameterType == type . MakeByRefType ( ) )
220
- {
221
- if ( type == typeof ( int ) || type == typeof ( short ) || type == typeof ( IntPtr ) ||
222
- type == typeof ( long ) || type == typeof ( byte ) || type == typeof ( sbyte ) ||
223
- type == typeof ( ushort ) || type == typeof ( uint ) || type == typeof ( ulong ) ||
224
- type == typeof ( BigInteger ) )
225
- {
226
- numberStylesToUse = NumberStyles . Integer ;
227
- }
201
+ method = typeof ( ulong ) . GetMethod (
202
+ nameof ( ulong . TryParse ) ,
203
+ BindingFlags . Public | BindingFlags . Static ,
204
+ new [ ] { typeof ( string ) , typeof ( NumberStyles ) , typeof ( IFormatProvider ) , typeof ( ulong ) . MakeByRefType ( ) } ) ;
205
+ }
206
+ else if ( type == typeof ( int ) )
207
+ {
208
+ method = typeof ( int ) . GetMethod (
209
+ nameof ( int . TryParse ) ,
210
+ BindingFlags . Public | BindingFlags . Static ,
211
+ new [ ] { typeof ( string ) , typeof ( NumberStyles ) , typeof ( IFormatProvider ) , typeof ( int ) . MakeByRefType ( ) } ) ;
212
+ }
213
+ else if ( type == typeof ( uint ) )
214
+ {
215
+ method = typeof ( uint ) . GetMethod (
216
+ nameof ( uint . TryParse ) ,
217
+ BindingFlags . Public | BindingFlags . Static ,
218
+ new [ ] { typeof ( string ) , typeof ( NumberStyles ) , typeof ( IFormatProvider ) , typeof ( uint ) . MakeByRefType ( ) } ) ;
219
+ }
220
+ else if ( type == typeof ( short ) )
221
+ {
222
+ method = typeof ( short ) . GetMethod (
223
+ nameof ( short . TryParse ) ,
224
+ BindingFlags . Public | BindingFlags . Static ,
225
+ new [ ] { typeof ( string ) , typeof ( NumberStyles ) , typeof ( IFormatProvider ) , typeof ( short ) . MakeByRefType ( ) } ) ;
226
+ }
227
+ else if ( type == typeof ( ushort ) )
228
+ {
229
+ method = typeof ( ushort ) . GetMethod (
230
+ nameof ( ushort . TryParse ) ,
231
+ BindingFlags . Public | BindingFlags . Static ,
232
+ new [ ] { typeof ( string ) , typeof ( NumberStyles ) , typeof ( IFormatProvider ) , typeof ( ushort ) . MakeByRefType ( ) } ) ;
233
+ }
234
+ else if ( type == typeof ( byte ) )
235
+ {
236
+ method = typeof ( byte ) . GetMethod (
237
+ nameof ( byte . TryParse ) ,
238
+ BindingFlags . Public | BindingFlags . Static ,
239
+ new [ ] { typeof ( string ) , typeof ( NumberStyles ) , typeof ( IFormatProvider ) , typeof ( byte ) . MakeByRefType ( ) } ) ;
240
+ }
241
+ else if ( type == typeof ( sbyte ) )
242
+ {
243
+ method = typeof ( sbyte ) . GetMethod (
244
+ nameof ( sbyte . TryParse ) ,
245
+ BindingFlags . Public | BindingFlags . Static ,
246
+ new [ ] { typeof ( string ) , typeof ( NumberStyles ) , typeof ( IFormatProvider ) , typeof ( sbyte ) . MakeByRefType ( ) } ) ;
247
+ }
248
+ else if ( type == typeof ( double ) )
249
+ {
250
+ method = typeof ( double ) . GetMethod (
251
+ nameof ( double . TryParse ) ,
252
+ BindingFlags . Public | BindingFlags . Static ,
253
+ new [ ] { typeof ( string ) , typeof ( NumberStyles ) , typeof ( IFormatProvider ) , typeof ( double ) . MakeByRefType ( ) } ) ;
228
254
229
- if ( type == typeof ( double ) || type == typeof ( float ) || type == typeof ( Half ) )
230
- {
231
- numberStylesToUse = NumberStyles . AllowThousands | NumberStyles . Float ;
232
- }
255
+ numberStyles = NumberStyles . AllowThousands | NumberStyles . Float ;
256
+ }
257
+ else if ( type == typeof ( float ) )
258
+ {
259
+ method = typeof ( float ) . GetMethod (
260
+ nameof ( float . TryParse ) ,
261
+ BindingFlags . Public | BindingFlags . Static ,
262
+ new [ ] { typeof ( string ) , typeof ( NumberStyles ) , typeof ( IFormatProvider ) , typeof ( float ) . MakeByRefType ( ) } ) ;
233
263
234
- if ( type == typeof ( decimal ) )
235
- {
236
- numberStylesToUse = NumberStyles . Number ;
237
- }
264
+ numberStyles = NumberStyles . AllowThousands | NumberStyles . Float ;
265
+ }
266
+ else if ( type == typeof ( Half ) )
267
+ {
268
+ method = typeof ( Half ) . GetMethod (
269
+ nameof ( Half . TryParse ) ,
270
+ BindingFlags . Public | BindingFlags . Static ,
271
+ new [ ] { typeof ( string ) , typeof ( NumberStyles ) , typeof ( IFormatProvider ) , typeof ( Half ) . MakeByRefType ( ) } ) ;
238
272
239
- methodToUse = methodInfo ! ;
240
- break ;
241
- }
273
+ numberStyles = NumberStyles . AllowThousands | NumberStyles . Float ;
242
274
}
275
+ else if ( type == typeof ( decimal ) )
276
+ {
277
+ method = typeof ( decimal ) . GetMethod (
278
+ nameof ( decimal . TryParse ) ,
279
+ BindingFlags . Public | BindingFlags . Static ,
280
+ new [ ] { typeof ( string ) , typeof ( NumberStyles ) , typeof ( IFormatProvider ) , typeof ( decimal ) . MakeByRefType ( ) } ) ;
243
281
244
- numberStyles = numberStylesToUse ! ;
245
- method = methodToUse ! ;
282
+ numberStyles = NumberStyles . Number ;
283
+ }
284
+ else if ( type == typeof ( IntPtr ) )
285
+ {
286
+ method = typeof ( IntPtr ) . GetMethod (
287
+ nameof ( IntPtr . TryParse ) ,
288
+ BindingFlags . Public | BindingFlags . Static ,
289
+ new [ ] { typeof ( string ) , typeof ( NumberStyles ) , typeof ( IFormatProvider ) , typeof ( IntPtr ) . MakeByRefType ( ) } ) ;
290
+ }
291
+ else if ( type == typeof ( BigInteger ) )
292
+ {
293
+ method = typeof ( BigInteger ) . GetMethod (
294
+ nameof ( BigInteger . TryParse ) ,
295
+ BindingFlags . Public | BindingFlags . Static ,
296
+ new [ ] { typeof ( string ) , typeof ( NumberStyles ) , typeof ( IFormatProvider ) , typeof ( BigInteger ) . MakeByRefType ( ) } ) ;
297
+ }
246
298
247
- return true ;
299
+ return method != null ;
248
300
}
249
-
250
- internal static bool UseTryParseWithNumberStyleOption ( Type type )
251
- => type == typeof ( int ) ||
252
- type == typeof ( double ) ||
253
- type == typeof ( decimal ) ||
254
- type == typeof ( float ) ||
255
- type == typeof ( Half ) ||
256
- type == typeof ( short ) ||
257
- type == typeof ( long ) ||
258
- type == typeof ( IntPtr ) ||
259
- type == typeof ( byte ) ||
260
- type == typeof ( sbyte ) ||
261
- type == typeof ( ushort ) ||
262
- type == typeof ( uint ) ||
263
- type == typeof ( ulong ) ||
264
- type == typeof ( BigInteger ) ;
265
301
}
266
302
}
0 commit comments