Buscar..
Reflexión robusta utilizando citas de F #
La reflexión es útil pero frágil. Considera esto:
let mi = typeof<System.String>.GetMethod "StartsWith"
Los problemas con este tipo de código son:
- El código no funciona porque hay varias sobrecargas de
String.StartsWith
- Incluso si no hubiera sobrecargas en este momento, las versiones posteriores de la biblioteca podrían agregar una sobrecarga que cause un bloqueo en el tiempo de ejecución
- Las herramientas de refactorización como los
Rename methods
se rompen con la reflexión.
Esto significa que tenemos un bloqueo en tiempo de ejecución para algo que se conoce como tiempo de compilación. Eso parece subóptimo.
Usando citas de F#
es posible evitar todos los problemas anteriores. Definimos algunas funciones de ayuda:
open FSharp.Quotations
open System.Reflection
let getConstructorInfo (e : Expr<'T>) : ConstructorInfo =
match e with
| Patterns.NewObject (ci, _) -> ci
| _ -> failwithf "Expression has the wrong shape, expected NewObject (_, _) instead got: %A" e
let getMethodInfo (e : Expr<'T>) : MethodInfo =
match e with
| Patterns.Call (_, mi, _) -> mi
| _ -> failwithf "Expression has the wrong shape, expected Call (_, _, _) instead got: %A" e
Usamos las funciones como esta:
printfn "%A" <| getMethodInfo <@ "".StartsWith "" @>
printfn "%A" <| getMethodInfo <@ List.singleton 1 @>
printfn "%A" <| getConstructorInfo <@ System.String [||] @>
Esto imprime:
Boolean StartsWith(System.String)
Void .ctor(Char[])
Microsoft.FSharp.Collections.FSharpList`1[System.Int32] Singleton[Int32](Int32)
<@ ... @>
significa que, en lugar de ejecutar la expresión dentro de F#
genera un árbol de expresión que representa la expresión. <@ "".StartsWith "" @>
genera un árbol de expresiones que se ve así: Call (Some (Value ("")), StartsWith, [Value ("")])
. Este árbol de expresiones coincide con lo que espera getMethodInfo
y devolverá la información del método correcto.
Esto resuelve todos los problemas mencionados anteriormente.