Szukaj…
Solidne odbicie przy użyciu cytatów F #
Odbicie jest przydatne, ale delikatne. Rozważ to:
let mi = typeof<System.String>.GetMethod "StartsWith"
Problemy z tego rodzaju kodem są następujące:
- Kod nie działa, ponieważ istnieje kilka przeciążeń
String.StartsWith
- Nawet jeśli w tej chwili nie wystąpiłyby żadne przeciążenia, późniejsze wersje biblioteki mogłyby dodać przeciążenie powodujące awarię środowiska wykonawczego
- Narzędzia do refaktoryzacji, takie jak
Rename methods
są zepsute.
Oznacza to, że otrzymujemy awarie środowiska wykonawczego dla czegoś, co jest znane z czasu kompilacji. To wydaje się nieoptymalne.
Za pomocą cytatów F#
można uniknąć wszystkich powyższych problemów. Definiujemy niektóre funkcje pomocnicze:
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
Korzystamy z takich funkcji:
printfn "%A" <| getMethodInfo <@ "".StartsWith "" @>
printfn "%A" <| getMethodInfo <@ List.singleton 1 @>
printfn "%A" <| getConstructorInfo <@ System.String [||] @>
To drukuje:
Boolean StartsWith(System.String)
Void .ctor(Char[])
Microsoft.FSharp.Collections.FSharpList`1[System.Int32] Singleton[Int32](Int32)
<@ ... @>
oznacza, że zamiast wykonywania wyrażenia w F#
generuje drzewo wyrażeń reprezentujące wyrażenie. <@ "".StartsWith "" @>
generuje drzewo wyrażeń, które wygląda następująco: Call (Some (Value ("")), StartsWith, [Value ("")])
. To drzewo wyrażeń jest zgodne z oczekiwaniami getMethodInfo
i zwróci prawidłowe informacje o metodzie.
To rozwiązuje wszystkie problemy wymienione powyżej.