Recherche…
Réflexion robuste à l'aide de citations F #
La réflexion est utile mais fragile. Considère ceci:
let mi = typeof<System.String>.GetMethod "StartsWith"
Les problèmes avec ce type de code sont les suivants:
- Le code ne fonctionne pas car il y a plusieurs surcharges de
String.StartsWith
- Même s'il n'y avait pas de surcharge, les versions ultérieures de la bibliothèque pourraient ajouter une surcharge provoquant un plantage à l'exécution
- Les outils de refactoring comme les
Rename methods
sont rompus avec la réflexion.
Cela signifie que nous obtenons un crash à l'exécution pour quelque chose qui est connu à la compilation. Cela semble sous-optimal.
En utilisant les citations F#
il est possible d'éviter tous les problèmes ci-dessus. Nous définissons des fonctions d'assistance:
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
Nous utilisons les fonctions comme ceci:
printfn "%A" <| getMethodInfo <@ "".StartsWith "" @>
printfn "%A" <| getMethodInfo <@ List.singleton 1 @>
printfn "%A" <| getConstructorInfo <@ System.String [||] @>
Cela imprime:
Boolean StartsWith(System.String)
Void .ctor(Char[])
Microsoft.FSharp.Collections.FSharpList`1[System.Int32] Singleton[Int32](Int32)
<@ ... @>
signifie qu'au lieu d'exécuter l'expression à l'intérieur de F#
génère un arbre d'expression représentant l'expression. <@ "".StartsWith "" @>
génère un arbre d'expression qui ressemble à ceci: Call (Some (Value ("")), StartsWith, [Value ("")])
. Cet arbre d'expression correspond à ce que getMethodInfo
attend et renvoie les informations de méthode correctes.
Cela résout tous les problèmes énumérés ci-dessus.