Zoeken…
Robuuste reflectie met behulp van F # citaten
Reflectie is nuttig maar fragiel. Overweeg dit:
let mi = typeof<System.String>.GetMethod "StartsWith"
De problemen met dit soort code zijn:
- De code werkt niet omdat er verschillende overbelastingen van
String.StartsWith
- Zelfs als er op dit moment geen overbelastingen zouden zijn, zouden latere versies van de bibliotheek een overbelasting kunnen toevoegen die een runtime-crash veroorzaakt
- Refactoring tools zoals
Rename methods
worden verbroken door reflectie.
Dit betekent dat we runtime-crashes krijgen voor iets waarvan bekend is dat het compileertijd is. Dat lijkt suboptimaal.
Met behulp van F#
citaten is het mogelijk om alle bovenstaande problemen te voorkomen. We definiëren enkele helpfuncties:
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
We gebruiken de volgende functies:
printfn "%A" <| getMethodInfo <@ "".StartsWith "" @>
printfn "%A" <| getMethodInfo <@ List.singleton 1 @>
printfn "%A" <| getConstructorInfo <@ System.String [||] @>
Dit drukt af:
Boolean StartsWith(System.String)
Void .ctor(Char[])
Microsoft.FSharp.Collections.FSharpList`1[System.Int32] Singleton[Int32](Int32)
<@ ... @>
betekent dat in plaats van de uitdrukking in F#
, een uitdrukkingsboom wordt gegenereerd die de uitdrukking vertegenwoordigt. <@ "".StartsWith "" @>
genereert een expressieboom die er zo uitziet: Call (Some (Value ("")), StartsWith, [Value ("")])
. Deze expressieboom komt overeen met wat getMethodInfo
verwacht en geeft de juiste methode-informatie terug.
Hiermee worden alle hierboven genoemde problemen opgelost.