1
- using System . Reflection ;
1
+ using System . Globalization ;
2
+ using System . Reflection ;
2
3
3
4
namespace Reflector ;
4
5
@@ -8,6 +9,95 @@ public static class CastExtensions
8
9
private const string ImplicitCastMethodName = "op_Implicit" ;
9
10
private const string ExplicitCastMethodName = "op_Explicit" ;
10
11
12
+ public static T ? SafeCast < T > ( this object obj ) where T : class
13
+ {
14
+ return obj as T ;
15
+ }
16
+
17
+ public static T CastOrDefault < T > ( this object obj , T defaultValue = default )
18
+ {
19
+ try
20
+ {
21
+ return ( T ) obj ;
22
+ }
23
+ catch
24
+ {
25
+ return defaultValue ;
26
+ }
27
+ }
28
+
29
+ public static bool TryCast < T > ( this object obj , out T ? result ) where T : class
30
+ {
31
+ if ( obj == null )
32
+ {
33
+ result = default ;
34
+ return false ;
35
+ }
36
+
37
+ if ( obj is T castedObj )
38
+ {
39
+ result = castedObj ;
40
+ return true ;
41
+ }
42
+
43
+ if ( obj . CanExplicitCast < T > ( ) )
44
+ {
45
+ result = obj . ExplicitCast < T > ( ) ;
46
+ return true ;
47
+ }
48
+
49
+ result = default ;
50
+ return false ;
51
+ }
52
+
53
+
54
+ public static Func < T , T , bool > GetComparer < T > ( )
55
+ {
56
+ if ( typeof ( T ) . IsValueType )
57
+ {
58
+ // Avoid causing any boxing for value types
59
+ return ( actual , expected ) => EqualityComparer < T > . Default . Equals ( actual , expected ) ;
60
+ }
61
+
62
+ if ( typeof ( T ) != typeof ( object ) )
63
+ {
64
+ // CompareNumerics is only relevant for numerics boxed in an object.
65
+ return ( actual , expected ) => actual is null
66
+ ? expected is null
67
+ : expected is not null && EqualityComparer < T > . Default . Equals ( actual , expected ) ;
68
+ }
69
+
70
+ return ( actual , expected ) => actual is null
71
+ ? expected is null
72
+ : expected is not null
73
+ && ( EqualityComparer < T > . Default . Equals ( actual , expected ) || CompareNumerics ( actual , expected ) ) ;
74
+ }
75
+
76
+ public static bool IsCastableTo < T > ( this object obj )
77
+ {
78
+ return obj is T || obj . GetType ( ) . CanCast < T > ( ) ;
79
+ }
80
+
81
+ public static bool CheckCastCompatibility ( this Type sourceType , Type targetType )
82
+ {
83
+ return sourceType . IsAssignableFrom ( targetType ) || targetType . IsAssignableFrom ( sourceType ) ;
84
+ }
85
+
86
+ public static bool CanConvert ( object source , object target , Type sourceType , Type targetType )
87
+ {
88
+ try
89
+ {
90
+ var converted = source . ConvertTo ( targetType ) ;
91
+
92
+ return source . Equals ( converted . ConvertTo ( sourceType ) )
93
+ && converted . Equals ( target ) ;
94
+ }
95
+ catch
96
+ {
97
+ // Ignored
98
+ return false ;
99
+ }
100
+ }
11
101
public static bool CanCast < T > ( this Type baseType )
12
102
{
13
103
return baseType . CanImplicitCast < T > ( ) || baseType . CanExplicitCast < T > ( ) ;
@@ -65,7 +155,7 @@ private static bool CanCast<T>(this Type baseType, string castMethodName)
65
155
. Where ( mi => mi . Name == castMethodName && mi . ReturnType == targetType )
66
156
. Any ( mi =>
67
157
{
68
- ParameterInfo pi = mi . GetParameters ( ) . FirstOrDefault ( ) ;
158
+ ParameterInfo ? pi = mi . GetParameters ( ) . FirstOrDefault ( ) ;
69
159
return pi != null && pi . ParameterType == baseType ;
70
160
} ) ;
71
161
}
@@ -87,13 +177,31 @@ private static T Cast<T>(this object obj, string castMethodName)
87
177
. Where ( mi => mi . Name == castMethodName && mi . ReturnType == typeof ( T ) )
88
178
. SingleOrDefault ( mi =>
89
179
{
90
- ParameterInfo pi = mi . GetParameters ( ) . FirstOrDefault ( ) ;
180
+ ParameterInfo ? pi = mi . GetParameters ( ) . FirstOrDefault ( ) ;
91
181
return pi != null && pi . ParameterType == objType ;
92
182
} ) ;
93
183
if ( conversionMethod != null )
94
- return ( T ) conversionMethod . Invoke ( null , new [ ] { obj } ) ;
184
+ return ( T ) conversionMethod . Invoke ( null , new [ ] { obj } ) ! ;
95
185
else
96
186
throw new InvalidCastException ( $ "No method to cast { objType . FullName } to { typeof ( T ) . FullName } ") ;
97
187
}
188
+
189
+ private static bool CompareNumerics ( object actual , object expected )
190
+ {
191
+ Type expectedType = expected . GetType ( ) ;
192
+ Type actualType = actual . GetType ( ) ;
193
+
194
+ return actualType != expectedType
195
+ && actual . IsNumericType ( )
196
+ && expected . IsNumericType ( )
197
+ && CanConvert ( actual , expected , actualType , expectedType )
198
+ && CanConvert ( expected , actual , expectedType , actualType ) ;
199
+ }
200
+
201
+
202
+ private static object ConvertTo ( this object source , Type targetType )
203
+ {
204
+ return Convert . ChangeType ( source , targetType , CultureInfo . InvariantCulture ) ;
205
+ }
98
206
}
99
207
0 commit comments