Suche…
Robustes Nachdenken durch F # -Zitate
Reflexion ist nützlich, aber fragil. Bedenken Sie:
let mi = typeof<System.String>.GetMethod "StartsWith"
Die Probleme mit dieser Art von Code sind:
- Der Code funktioniert nicht, da
String.StartsWith
mehrere ÜberladungenString.StartsWith
- Selbst wenn es jetzt keine Überlastungen gibt, können spätere Versionen der Bibliothek eine Überlastung hinzufügen, die einen Laufzeitabsturz verursacht
- Refactoring-Tools wie
Rename methods
durch Reflektion unterbrochen.
Das heißt, wir bekommen eine Laufzeitabsturz für etwas, das als Kompilierzeit bekannt ist. Das scheint suboptimal zu sein.
Mit F#
-Zitate können Sie alle oben genannten Probleme vermeiden. Wir definieren einige Hilfsfunktionen:
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
Wir verwenden die Funktionen wie folgt:
printfn "%A" <| getMethodInfo <@ "".StartsWith "" @>
printfn "%A" <| getMethodInfo <@ List.singleton 1 @>
printfn "%A" <| getConstructorInfo <@ System.String [||] @>
Dies druckt:
Boolean StartsWith(System.String)
Void .ctor(Char[])
Microsoft.FSharp.Collections.FSharpList`1[System.Int32] Singleton[Int32](Int32)
<@ ... @>
bedeutet, dass anstelle der Ausführung des Ausdrucks in F#
eine Ausdrucksstruktur generiert wird, die den Ausdruck darstellt. <@ "".StartsWith "" @>
generiert eine Ausdrucksbaumstruktur, die folgendermaßen aussieht: Call (Some (Value ("")), StartsWith, [Value ("")])
. Diese Ausdrucksbaumstruktur entspricht den getMethodInfo
und gibt die korrekten Methodeninformationen zurück.
Dies löst alle oben aufgeführten Probleme.