F# infoof (part 3)
Наш модуль осталось дополнить лишь такими функциями, как fieldinfo
(реализация практически идентична propertyinfo
), constructorof
(получение System.Reflection.
ConstructorInfo
по выражению создания экземпляров объектов) и unioncaseof
(получение Microsoft.FSharp.Reflection.UnionCaseInfo
по выражению вызова конструктора типа-объдинения F#). Все они достаточно тривиальны, поэтому лишь привожу окончательную реализацию модуля MemberInfo
.
Сигнатура модуля:
module MemberInfo
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Reflection
open System.Reflection
val fieldof : Expr -> FieldInfo
val propertyof : Expr -> PropertyInfo
val methodof : Expr -> MethodInfo
val methoddefof : Expr -> MethodInfo
val constructorof : Expr -> ConstructorInfo
val unioncaseof : Expr -> UnionCaseInfo
val eventof : Expr -> EventInfo
Реализация модуля:
module MemberInfo
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns
open System.Reflection
/// Получение FieldInfo по выражению доступа к полю
let fieldof expr =
match expr with
| FieldGet(_, info) -> info
| Lambda(arg, FieldGet(Some(Var var), info))
| Let(arg, _, FieldGet(Some(Var var), info))
when arg = var -> info
| _ -> failwith "Not a field expression"
/// Получение PropertyInfo по выражению доступа к свойству
let propertyof expr =
match expr with
| PropertyGet(_, info, _) -> info
| Lambda(arg, PropertyGet(Some(Var var), info, _))
| Let(arg, _, PropertyGet(Some(Var var), info, _))
when arg = var -> info
| _ -> failwith "Not a property expression"
/// Образец, совпадающий со значением функционального
/// типа, сгенерированным F# из метода или функции
let private (|Func|_|) expr =
let onlyVar = function Var v -> Some v | _ -> None
match expr with
// функ.значения без аргументов
| Lambda(arg, Call(target, info, []))
when arg.Type = typeof<unit> -> Some(target, info)
// функ.значения с одним аргументом
| Lambda(arg, Call(target, info, [ Var var ]))
when arg = var -> Some(target, info)
// функ.значения с набором каррированных
// или взятых в кортеж аргументов
| Lambdas(args, Call(target, info, exprs))
when List.choose onlyVar exprs
= List.concat args -> Some(target, info)
| _ -> None
/// Получение MethodInfo по выражению вызова метода
/// или выражению значения функционального типа
let methodof expr =
match expr with
// любые обычные вызовы: foo.Bar()
| Call(_, info, _) -> info
// вызовы и функ.значения через аргумент лямбды:
// fun (x: string) -> x.Substring(1, 2)
// fun (x: string) -> x.StartsWith
| Lambda(arg, Call(Some(Var var), info, _))
| Lambda(arg, Func(Some(Var var), info))
when arg = var -> info
// любые функциональные значения:
// someString.StartsWith
| Func(_, info) -> info
// вызовы и функ.значения через экземпляры:
// "abc".StartsWith("a")
// "abc".Substring
| Let(arg, _, Call(Some (Var var), info, _))
| Let(arg, _, Func(Some (Var var), info))
when arg = var -> info
| _ -> failwith "Not a method expression"
/// Получение generic method definition по выражению
/// вызова generic-метода или выражению значения
/// функционального типа
let methoddefof expr =
match methodof expr with
| info when info.IsGenericMethod ->
info.GetGenericMethodDefinition()
| info -> failwithf "%A is not generic" info
/// Получение ConstructorInfo по выражению
/// создания нового объекта или записи F#
let constructorof expr =
match expr with
| NewObject(info, _) -> info
// Получения конструктора record'а
| NewRecord(recordType, _) ->
match recordType.GetConstructors() with
| [| info |] -> info
| _ -> failwith "Invalid record type"
| _ -> failwith "Not a constructor expression"
/// Получение EventInfo по выражению
/// доступа к CLI-совместимому событию
let eventof expr =
match expr with
| Call(None, createEvent, [
Lambda(arg1, Call(_, addHandler, [ Var var1 ]))
Lambda(arg2, Call(_, removeHandler, [ Var var2 ]))
Lambda(_, NewDelegate _)
])
when createEvent.Name = "CreateEvent"
&& addHandler.Name.StartsWith("add_")
&& removeHandler.Name.StartsWith("remove_")
&& arg1 = var1
&& arg2 = var2 ->
addHandler.DeclaringType.GetEvent(
addHandler.Name.Remove(0, 4),
BindingFlags.Public ||| BindingFlags.Instance |||
BindingFlags.Static ||| BindingFlags.NonPublic)
| _ -> failwith "Not a event expression"
/// Получение UnionCaseInfo по выражению вызова
/// или проверки конструктора union-типа
let unioncaseof expr =
match expr with
| NewUnionCase(info, _)
| UnionCaseTest(_, info) -> info
| _ -> failwith "Not a union case expression"