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 methodssono 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.