Reputation: 51
I am trying to find let bindings with a specific attribute and type, throughout a given assembly.
For instance, the following type and attribute:
type TargetType = { somedata: string }
type MarkingAttribute() = inherit System.Attribute()
Then I would like to find the value in the following module:
module SomeModule =
[<Marking>]
let valueIWantToFind = {somedata = "yoyo"}
So what I am looking for is a function with the following signature (assuming it is suitable for a generic function signature):
let valuesOfTypeWithAttribute<'t,'attr> (assembly: Assembly) : 't list = ...
My futile attempts seem to be blocked by my lack of understanding how F# modules are translated to CLR (CLI?) classes.
I have the following FSI snippet which unfortunately finds nothing:
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>)
Any help or pointers will be greatly appreciated.
Upvotes: 3
Views: 258
Reputation: 51
Mark's response led me onto the path of success. The reflection does not work for modules defined entirely in FSI (at least not for me in my setup).
The function I came up with looks like this:
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)
Upvotes: 2
Reputation: 233150
Modules are compiled as classes with static members. Load your assembly into a value called assembly
, and start to investigate:
> let publicTypes = assembly.GetExportedTypes ();;
val publicTypes : System.Type [] =
[|Ploeh.StackOverflow.Q36245870.TargetType;
Ploeh.StackOverflow.Q36245870.MarkingAttribute;
Ploeh.StackOverflow.Q36245870.SomeModule|]
As you can tell, SomeModule
is one of those types:
> let someModule =
publicTypes |> Array.find (fun t -> t.Name.EndsWith "SomeModule");;
val someModule : System.Type = Ploeh.StackOverflow.Q36245870.SomeModule
You can now get all members of the type:
> 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|]
This array includes the let-bound function valueIWantToFind
, and it has the desired attribute:
> let attrs = members.[5].GetCustomAttributes ();;
val attrs : System.Collections.Generic.IEnumerable<System.Attribute> =
[|Ploeh.StackOverflow.Q36245870.MarkingAttribute;
Microsoft.FSharp.Core.CompilationMappingAttribute|]
Upvotes: 3