diff --git a/TypeMagic.pas b/TypeMagic.pas new file mode 100644 index 00000000..fc72491a --- /dev/null +++ b/TypeMagic.pas @@ -0,0 +1,68 @@ +unit TypeMagic; + +interface + +type + + {$region Convert} + + ConvertTo = sealed class + + private constructor := raise new System.InvalidOperationException; + + public static function FromChecked(o: T2): T; + public static function FromUnchecked(o: T2): T; + public static function From(o: T2; checked: boolean := true) := + if checked then FromChecked(o) else FromUnchecked(o); + + end; + + {$endregion Convert} + +implementation + +uses System.Linq.Expressions; + +{$region Convert} + +type ConvertCache = sealed class + + private constructor := raise new System.InvalidOperationException; + + public static checked, unchecked: T2->T1; + private static function MakeFunc(conv: (Expression,System.Type)->Expression): T2->T1; + begin + try + var p := Expression.Parameter(typeof(T2)); + var c := conv(p, typeof(T1)); + var l := Expression.Lambda&>(c, p); + Result := l.Compile(); + except + on e: Exception do + begin + Result := o-> + begin + Result := default(T1); + raise new System.InvalidCastException($'Failed to make [{TypeToTypeName(typeof(T2))}]=>[{TypeToTypeName(typeof(T1))}] conversion', e); + end; + exit; + end; + end; + + end; + static constructor; + begin + checked := MakeFunc(Expression.ConvertChecked); + unchecked := MakeFunc(Expression.Convert); + end; + +end; + +static function ConvertTo.FromChecked(o: T2) := + ConvertCache&.checked(o); +static function ConvertTo.FromUnchecked(o: T2) := + ConvertCache&.unchecked(o); + +{$endregion Convert} + +end. \ No newline at end of file