按属性和类型反映模块中的 let 绑定

Reflecting let bindings in a module by attribute and type

我试图在整个给定程序集中查找具有特定属性和类型的 let 绑定。​​

例如,以下类型和属性:

type TargetType = { somedata: string }
type MarkingAttribute() = inherit System.Attribute()

那么我想在以下模块中查找值:

module SomeModule =
    [<Marking>]
    let valueIWantToFind = {somedata = "yoyo"} 

所以我正在寻找的是具有以下签名的函数(假设它适用于通用函数签名):

let valuesOfTypeWithAttribute<'t,'attr> (assembly: Assembly) : 't list = ...

我的徒劳尝试似乎因缺乏对 F# 模块如何转换为 CLR(CLI?)的理解而受阻类。

我有以下 FSI 片段,不幸的是没有找到:

open System.Reflection
let types = Assembly.GetExecutingAssembly().GetTypes()

let fiWithAttribute (attributeType: System.Type) (fi: FieldInfo) =
    fi.CustomAttributes
    |> Seq.exists (fun attr -> attr.AttributeType = attributeType)

let fields = 
    types 
    |> Array.collect (fun t -> t.GetFields())
    |> Array.filter (fiWithAttribute typeof<MarkingAttribute>)

任何帮助或指点将不胜感激。

模块被编译为 类 静态成员。将程序集加载到一个名为 assembly 的值中,然后开始调查:

> let publicTypes = assembly.GetExportedTypes ();;

val publicTypes : System.Type [] =
  [|Ploeh.Whosebug.Q36245870.TargetType;
    Ploeh.Whosebug.Q36245870.MarkingAttribute;
    Ploeh.Whosebug.Q36245870.SomeModule|]

如您所知,SomeModule 是以下类型之一:

> let someModule =
    publicTypes |> Array.find (fun t -> t.Name.EndsWith "SomeModule");;

val someModule : System.Type = Ploeh.Whosebug.Q36245870.SomeModule

您现在可以获得以下类型的所有成员:

> let members = someModule.GetMembers ();;

val members : MemberInfo [] =
  [|Ploeh.Whosebug.Q36245870.TargetType get_valueIWantToFind();
    System.String ToString(); Boolean Equals(System.Object);
    Int32 GetHashCode(); System.Type GetType();
    Ploeh.Whosebug.Q36245870.TargetType valueIWantToFind|]

这个数组包含 let-bound 函数 valueIWantToFind,它具有所需的属性:

> let attrs = members.[5].GetCustomAttributes ();;

val attrs : System.Collections.Generic.IEnumerable<System.Attribute> =
  [|Ploeh.Whosebug.Q36245870.MarkingAttribute;
    Microsoft.FSharp.Core.CompilationMappingAttribute|]

马克的回应让我走上了成功之路。反射不适用于完全在 FSI 中定义的模块(至少在我的设置中不适用于我)。

我想出的函数是这样的:

open Microsoft.FSharp.Reflection
let letBindingsWithTypeAndAttribute<'t,'attr> (assembly: Assembly) : 't array =
    let publicTypes = assembly.GetExportedTypes ()
    let modules = publicTypes |> Array.filter FSharpType.IsModule
    let members = modules |> Array.collect (fun m -> m.GetMembers ())

    let miHasAttribute (mi : MemberInfo) =
        mi.GetCustomAttributes () 
        |> Seq.exists (fun attr' -> attr'.GetType() = typeof<'attr>)

    let withAttr = 
        members 
        |> Array.filter miHasAttribute

    let valueOfBinding (mi : MemberInfo) =
        let property = mi.Name
        mi.DeclaringType.GetProperty(property).GetValue null

    withAttr 
        |> Array.map valueOfBinding 
        |> Array.choose (fun o ->  match o with
                                    | :? 't as x -> Some x
                                    | _ -> None)