1- using System . Reflection ;
1+ using System . Globalization ;
2+ using System . Reflection ;
23
34namespace Reflector ;
45
@@ -8,6 +9,95 @@ public static class CastExtensions
89 private const string ImplicitCastMethodName = "op_Implicit" ;
910 private const string ExplicitCastMethodName = "op_Explicit" ;
1011
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+ }
11101 public static bool CanCast < T > ( this Type baseType )
12102 {
13103 return baseType . CanImplicitCast < T > ( ) || baseType . CanExplicitCast < T > ( ) ;
@@ -65,7 +155,7 @@ private static bool CanCast<T>(this Type baseType, string castMethodName)
65155 . Where ( mi => mi . Name == castMethodName && mi . ReturnType == targetType )
66156 . Any ( mi =>
67157 {
68- ParameterInfo pi = mi . GetParameters ( ) . FirstOrDefault ( ) ;
158+ ParameterInfo ? pi = mi . GetParameters ( ) . FirstOrDefault ( ) ;
69159 return pi != null && pi . ParameterType == baseType ;
70160 } ) ;
71161 }
@@ -87,13 +177,31 @@ private static T Cast<T>(this object obj, string castMethodName)
87177 . Where ( mi => mi . Name == castMethodName && mi . ReturnType == typeof ( T ) )
88178 . SingleOrDefault ( mi =>
89179 {
90- ParameterInfo pi = mi . GetParameters ( ) . FirstOrDefault ( ) ;
180+ ParameterInfo ? pi = mi . GetParameters ( ) . FirstOrDefault ( ) ;
91181 return pi != null && pi . ParameterType == objType ;
92182 } ) ;
93183 if ( conversionMethod != null )
94- return ( T ) conversionMethod . Invoke ( null , new [ ] { obj } ) ;
184+ return ( T ) conversionMethod . Invoke ( null , new [ ] { obj } ) ! ;
95185 else
96186 throw new InvalidCastException ( $ "No method to cast { objType . FullName } to { typeof ( T ) . FullName } ") ;
97187 }
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+ }
98206}
99207
0 commit comments