给定一些函数test1
,test2
,...属于一个模块:
module Checks =
let test1 x = ...
let test2 x = ...
...
如何使用(?)运算符来访问函数名和函数本身?结果应如下所示:
let name, func = Checks?test1
assert(name = "test1")
assert(func(x) = Checks.test1(x)) //whatever x is (test1 is known to be pure)
答案 0 :(得分:3)
您不能使用?
运算符来访问模块中的函数,因为构造Checks?test1
在语法上不正确(这将转换为(?) Checks "test"
并且您不能使用模块名称作为值)。
但是,应该可以使用对象的实例(例如obj?test
)对类型的成员执行此操作。或者,您可以编写一个“假”对象实例(它知道模块的名称)。然后,?
的实现将查找模块并搜索模块中的静态成员。
最简单的实现(第一种情况)看起来像这样:
let (?) obj s =
let memb = obj.GetType().GetMethod(s)
// Return name and a function that runs the method
s, (fun args -> memb.Invoke(obj, args))
// Type that contains tests as members
type Check() =
member x.test1 () = 32
// We need to create instance in order to use '?'
let ch = Check()
let s,f = ch?test1
// Function 'f' takes array of objects as an argument and
// returns object, so the call is not as elegant as it could be
let n = ((f [| |]) :?> int)
你也可以添加一些包装来使函数'f'更好一些,但我希望这能证明这个想法。不幸的是,这不适用于模块。
答案 1 :(得分:1)
这里有一些示例代码,展示了其中的一些内容。我使用D
作为Checks
模块加上函数名称的“动态”访问。
module Checks =
let test1(x) = printfn "test1 %d" x
let test2(x,y) = printfn "test2 %s %d" x y
type MyDynamic() = class end
let D = new MyDynamic()
let (?) (md:MyDynamic) fname : (string * ('a -> 'r)) =
let a = md.GetType().Assembly
let t = a.GetType("Program+Checks")
let m = t.GetMethod(fname)
let f arg =
let at = arg.GetType()
let fsharpArgs =
if at.IsGenericType && at.GetGenericTypeDefinition().FullName.StartsWith("System.Tuple`") then
Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields(arg)
else
[| box arg |]
unbox(m.Invoke(null, fsharpArgs))
fname, f
let Main() =
let x = 42
let s = "foo"
let name, func = D?test1
assert(name = "test1")
assert(func(x) = Checks.test1(x))
let name, func = D?test2
assert(name = "test2")
assert(func(s,x) = Checks.test2(s,x))
System.Console.ReadKey()
Main()