Ricerca…
Riflessione robusta usando le citazioni di F #
La riflessione è utile ma fragile. Considera questo:
let mi = typeof<System.String>.GetMethod "StartsWith"
I problemi con questo tipo di codice sono:
- Il codice non funziona perché ci sono diversi overload di
String.StartsWith
- Anche se non ci sarebbero sovraccarichi in questo momento, le versioni successive della libreria potrebbero aggiungere un sovraccarico che causa un arresto anomalo del runtime
- Strumenti di refactoring come i
Rename methods
sono infranti di riflessione.
Ciò significa che si verifica un arresto anomalo del runtime per qualcosa che è noto in fase di compilazione. Sembra inopportuno.
Utilizzando le citazioni di F#
è possibile evitare tutti i problemi di cui sopra. Definiamo alcune funzioni di supporto:
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
Usiamo le funzioni in questo modo:
printfn "%A" <| getMethodInfo <@ "".StartsWith "" @>
printfn "%A" <| getMethodInfo <@ List.singleton 1 @>
printfn "%A" <| getConstructorInfo <@ System.String [||] @>
Questo stampa:
Boolean StartsWith(System.String)
Void .ctor(Char[])
Microsoft.FSharp.Collections.FSharpList`1[System.Int32] Singleton[Int32](Int32)
<@ ... @>
significa che invece di eseguire l'espressione all'interno di F#
genera un albero di espressioni che rappresenta l'espressione. <@ "".StartsWith "" @>
genera un albero di espressioni simile al seguente: Call (Some (Value ("")), StartsWith, [Value ("")])
. Questo albero di espressioni corrisponde a ciò che getMethodInfo
aspetta e restituirà le informazioni sul metodo corretto.
Questo risolve tutti i problemi sopra elencati.