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> 将采用源中定义的符号名称,而不是调用站点中替换的类型的名称。

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

模式匹配中的 Nameof

nameof 模式 允许在模式匹配表达式中使用 nameof 。 这在将字符串值与代码中的符号名称匹配时特别有用,在重构时提供编译时安全和自动更新。

一个实际示例是反序列化事件或消息,其中字符串值表示类型或大小写名称:

type EventType =
    | OrderCreated
    | OrderShipped
    | OrderDelivered

let handleEvent eventName data =
    match eventName with
    | nameof OrderCreated -> printfn "Processing order creation: %s" data
    | nameof OrderShipped -> printfn "Processing order shipment: %s" data
    | nameof OrderDelivered -> printfn "Processing order delivery: %s" data
    | _ -> printfn "Unknown event type: %s" eventName

handleEvent "OrderCreated" "Order #123" // matches first case

使用 nameof 而不是字符串文本(如 "OrderCreated" 提供以下几个优势):

  • 如果重命名受歧视的联合案例,则模式会自动更新。
  • 编译器通过确保符号存在来防止拼写错误。
  • 在重构期间,代码保持一致。

还可以与参数一起使用 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 如果实例不可用,则可以使用 < a0/a0> 获取实例。

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