Nameof

nameof 表达式生成一个字符串常数,该常数与源中几乎任何 F# 构造的名称相匹配。

语法

nameof symbol
nameof<'TGeneric>

备注

nameof 的工作原理是解析传递给它的符号,并生成该符号在源代码中声明的名称。 这可用于各种方案(如日志记录)并保护日志记录免受源代码更改的影响。

let months =
    [
        "January"; "February"; "March"; "April";
        "May"; "June"; "July"; "August"; "September";
        "October"; "November"; "December"
    ]

let lookupMonth month =
    if (month > 12 || month < 1) then
        invalidArg (nameof month) ($"Value passed in was %d{month}.")

    months[month-1]

printfn "%s" (lookupMonth 12)
printfn "%s" (lookupMonth 1)
printfn "%s" (lookupMonth 13)

最后一行会引发异常,错误消息中会显示 "month"

可以采用几乎每个 F# 构造的名称:

module M =
    let f x = nameof x

printfn $"{(M.f 12)}"
printfn $"{(nameof M)}"
printfn $"{(nameof M.f)}"

nameof 不是第一类函数,不能这样使用。 这意味着它不能部分应用,也不能通过 F# 管道运算符将值导入其中。

运算符上的 Nameof

F# 中的运算符可以以两种方式使用,作为运算符文本本身,或作为表示编译形式的符号。 运算符上的 nameof 将生成在源中声明的运算符的名称。 若要获取已编译的名称,请使用源中编译的名称:

nameof(+) // "+"
nameof op_Addition // "op_Addition"

泛型上的 Nameof

也可以使用泛型类型参数的名称,但语法不同:

let f<'a> () = nameof<'a>
f() // "a"

nameof<'TGeneric> 将采用源中定义的符号名称,而不是在调用站点替换的类型名称。

语法不同的原因是与 typeof<>typedefof<> 等其他 F# 内部运算符对齐。 这使得 F# 与作用于泛型类型和源中的任何其他内容的运算符保持一致。

模式匹配中的 Nameof

nameof 模式 允许你在模式匹配表达式中使用 nameof,如下所示:

let f (str: string) =
    match str with
    | nameof str -> "It's 'str'!"
    | _ -> "It is not 'str'!"

f "str" // matches
f "asdf" // does not match

具有实例成员的 Nameof

F# 需要一个实例才能使用 nameof 提取实例成员的名称。 如果实例不容易获得,则可以使用 Unchecked.defaultof 获得。

type MyRecord = { MyField: int }
type MyClass() =
    member _.MyProperty = ()
    member _.MyMethod () = ()

nameof Unchecked.defaultof<MyRecord>.MyField   // MyField
nameof Unchecked.defaultof<MyClass>.MyProperty // MyProperty
nameof Unchecked.defaultof<MyClass>.MyMethod   // MyMethod