With propertyof, methodof, methoddefof, and eventof covered in the preceding posts, we can now assemble the complete MemberInfo module. Three helpers remain: fieldof, whose implementation closely follows propertyof; constructorof, which returns a System.Reflection.ConstructorInfo from an object creation expression; and unioncaseof, which returns a Microsoft.FSharp.Reflection.UnionCaseInfo from a union case construction or test expression. Their implementations are straightforward and are included in the full module below.

Module signature:

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 implementation:

module MemberInfo

open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns
open System.Reflection

/// Return FieldInfo for a field access expression.
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"

/// Return PropertyInfo for a property access expression.
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"

/// Match a function value generated by F#
/// from a method or function.
let private (|Func|_|) expr =
  let onlyVar = function Var v -> Some v | _ -> None
  match expr with
    // function values for methods with no parameters
    | Lambda(arg, Call(target, info, []))
        when arg.Type = typeof<unit> -> Some(target, info)

    // function values with one argument
    | Lambda(arg, Call(target, info, [ Var var ]))
        when arg = var -> Some(target, info)

    // function values with curried
    // or tupled arguments
    | Lambdas(args, Call(target, info, exprs))
        when List.choose onlyVar exprs
           = List.concat args -> Some(target, info)

    | _ -> None

/// Return MethodInfo for a method call
/// or a function value expression.
let methodof expr =
  match expr with
    // ordinary calls: foo.Bar()
    | Call(_, info, _) -> info

    // calls and function values through a lambda parameter:
    // 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

    // function values:
    // someString.StartsWith
    | Func(_, info) -> info

    // calls and function values through instance expressions:
    // "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"

/// Return a generic method definition from
/// a generic method call or
/// a function value expression.
let methoddefof expr =
  match methodof expr with
    | info when info.IsGenericMethod ->
                info.GetGenericMethodDefinition()
    | info -> failwithf "%A is not generic" info

/// Return ConstructorInfo for an expression
/// creating an object or an F# record.
let constructorof expr =
  match expr with
    | NewObject(info, _) -> info

    // get the record constructor
    | NewRecord(recordType, _) ->
        match recordType.GetConstructors() with
        | [| info |] -> info
        | _ -> failwith "Invalid record type"

    | _ -> failwith "Not a constructor expression"

/// Return EventInfo for an expression
/// accessing a CLI-compatible event.
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 an event expression"

/// Return UnionCaseInfo for a union case
/// construction or test expression.
let unioncaseof expr =
  match expr with
    | NewUnionCase(info, _)
    | UnionCaseTest(_, info) -> info
    | _ -> failwith "Not a union case expression"