Retrieving attributes from a function argument
If I have a function that takes in another function:
[<Some开发者_如何学编程Attribute()>]
let f (g:unit->unit) =
//Want to get g's custom attributes
How can I access g's custom attributes from f?
I think I'm missing something really obvious here.
This is not in general possible, because when you use a function as an argument (e.g. f foo
), the F# compiler wraps the foo
value into some object. Extracting the actual method reference foo
from this object would be very difficult (and it would work only if the compiler didn't do some optimizations).
However, you can get the desired behavior using F# quotations. Instead of taking a function unit -> unit
, your f
can take a quoted function Expr<unit -> unit>
. You can then call the function using f <@ foo @>
and the function can extract the method refernce and also call foo
.
Here is an example. It requires reference to F# PowerPack (so that it can evaluate the quotation). In this simple case, the evaluation should be quite efficient:
#r @"FSharp.PowerPack.Linq.dll"
type SomeAttribute(name:string) =
inherit System.Attribute()
member x.Name = name
// Example function with some attribute
[<SomeAttribute("Test")>]
let g () = printfn "Hello"
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.QuotationEvaluation
// Takes a quotation instead of a function value
let f (g:Expr<unit->unit>) =
// Extract method info & attributes from the quotation
match g with
| DerivedPatterns.Lambdas(_, Patterns.Call(_, mi, _)) ->
let attrs = mi.GetCustomAttributes(typeof<SomeAttribute>, false)
for a in attrs |> Seq.cast<SomeAttribute> do
printfn "%A" a.Name
| _ ->
failwith "Argument must be of the form <@ foo @>!"
// Compile the function so that it can be executed (the compilation
// takes some time, but calling invoke should be fast)
let invoke = g.Compile()()
invoke()
invoke()
// And this is how you call the function
f <@ g @>
let f (g:unit->unit) =
printfn "%d" (g.GetType().GetCustomAttributes(true).Count())
精彩评论