轻松获取F#中的函数名称

rmu*_*unn 3 reflection f#

在寻找答案时,我发现了几个与我的情况不太匹配的问题 - 所以我会问一个新问题.

我正在为数据结构编写一些FsCheck测试,我想在我构建的数据结构的每个副本上检查20个不同的属性.到目前为止我所做的是为每个属性编写一个谓词,然后我创建了一个谓词列表,我将依次调用它们List.forall,如下所示:

module PropertyChecks =
    let ``Tail length plus tree length should equal vector length`` vec =
        treeLength vec.root + Array.length vec.tail = vec.len

    let ``The tail may only be empty iff the vector is empty`` vec =
        (vec.len = 0) = (Array.length vec.tail = 0)

    let ``The tail's length may not exceed tailMax`` vec =
        Array.length vec.tail < tailMax

    let ``If vec.len <= tailMax, all items are in tail and root is empty`` vec =
        (vec.len > tailMax) || (Array.length vec.root = 0)

    // And so on for about 15 more predicates

    let allProperties =
      [
        ``Tail length plus tree length should equal vector length``
        ``The tail may only be empty iff the vector is empty``
        ``The tail's length may not exceed tailMax``
        ``If vec.len <= tailMax, all items are in tail and root is empty``
      ]

    let checkProperties vec =
        allProperties |> List.forall (fun pred -> pred vec)

    // Rest of my code omitted since it's not relevant to the question
Run Code Online (Sandbox Code Playgroud)

我面临的问题是,我希望当一个属性因为我没有正确构造数据结构而失败时,两个或三个其他属性将同时失败.我想获得所有失败属性的列表,这意味着checkProperties我想提取失败谓词的名称.我已经看到了多个答案:"你无法MethodInfo从作为参数获得的任意F#函数中获取,因为你永远不知道你是否拥有函数本身,或者是lambda,还是匿名函数".但在这里,我不仅知道我有真正的功能,我知道他们的名字是什么.我可以轻松地将其名称复制并粘贴到字符串中,并allProperties列出(字符串,函数)元组.但是我已经对复制和粘贴一次(将谓词放入该列表)感到不满意,而我宁愿不做两次.

制作(函数名称,函数)元组列表的更好的解决方案是什么?我可以移动allPropertiescheckProperties移出PropertyChecks模块,使它只包含谓词,然后使用反射来走动该模块吗?

我对整个.Net反射系统都很陌生,所以我很可能会遗漏一些明显的东西.如果我错过了,请随时指出明显的; 我不会感到被侮辱.

Tom*_*cek 5

运行FsCheck测试的最佳选择是将FsCheck与一些单元测试运行器一起使用.测试运行器负责查找所有属性,运行它们并在出现问题时打印漂亮的错误日志.

.NET中与FsCheck一起使用的两个最常见的测试运行器是xUnit和NUnit,FsCheck文档描述了如何使用它们:

在这两种情况下,您[<Property>]都使用属性标记属性,测试运行器将使用它来查找它们并使用FsCheck为您运行它们.所以你需要这样的东西:

[<Property>]
let ``Tail length plus tree length should equal vector length`` vec =
    treeLength vec.root + Array.length vec.tail = vec.len

[<Property>]
let ``The tail may only be empty iff the vector is empty`` vec =
    (vec.len = 0) = (Array.length vec.tail = 0)

[<Property>]
let ``The tail's length may not exceed tailMax`` vec =
    Array.length vec.tail < tailMax

[<Property>]
let ``If vec.len <= tailMax, all items are in tail and root is empty`` vec =
    (vec.len > tailMax) || (Array.length vec.root = 0)
Run Code Online (Sandbox Code Playgroud)