通过属性和类型

时间:2016-03-27 09:34:24

标签: f# system.reflection

我试图在给定的程序集中找到具有特定属性和类型的绑定。

例如,以下类型和属性:

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>)

非常感谢任何帮助或指示。

2 个答案:

答案 0 :(得分:3)

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

> let publicTypes = assembly.GetExportedTypes ();;

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

正如您所知,SomeModule是其中一种类型:

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

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

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

> let members = someModule.GetMembers ();;

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

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

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

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

答案 1 :(得分:2)

马克的回应让我走上了成功的道路。反射不适用于完全在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)