自动找出f#中的函数名称

时间:2018-09-28 11:15:38

标签: logging f# functional-programming

如果我有一个函数是模块的一部分,并且我希望在函数内部有一个日志条目,则必须手动打印函数名称空间和名称,例如

namespace MyModuleNamespace
 module MyModule = 
 let AddTwoNums logger x y =
    logger.Info("MyModuleNamespace.AddTwoNums - Start")
    let res = x+y
    logger.Info("MyModuleNamespace.AddTwoNums - End")
    res

有什么方法可以自动计算出“ MyModuleNamespace.AddTwoNums”是什么,因为它非常麻烦/容易出错,尤其是当您在代码重构期间必须重命名函数和模块时

即使无法做到,我还有什么方法可以自动确定“ AddTwoNums”是什么,即函数名称?

3 个答案:

答案 0 :(得分:7)

有几种方法可以做到这一点,我不确定哪种方法最适合您的情况。我过去使用的一种方法是从堆栈跟踪中获取信息:

let stackTrace = StackTrace()
let topFrame = stackTrace.GetFrame(0)
let currentFunction = topFrame.GetMethod()
printfn "%s.%s" currentFunction.DeclaringType.Name currentFunction.Name

为了避免在每个函数中都放置这些行,您可以制作一个inline函数来执行此操作,由于内联,因此将为您提供调用函数的名称。

let inline getCurrentFunction () =
    let stackTrace = StackTrace()
    let topFrame = stackTrace.GetFrame(0)
    let currentFunction = topFrame.GetMethod()
    sprintf "%s.%s" currentFunction.DeclaringType.Name currentFunction.Name

答案 1 :(得分:4)

如果您不需要实际的名称空间/模块名称或愿意用文件名替换它,则可以使用一些特殊的属性来指示编译器向函数提供相应的参数:

open System.Runtime.CompilerServices

type Logger =
    static member Trace(
                        msg: string,
                        [<CallerMemberName>] ?memberName: string,
                        [<CallerFilePath>] ?path: string,
                        [<CallerLineNumberAttribute>] ?line: int) =
        printfn "%s from %s.%s on line %i" msg path.Value memberName.Value line.Value

module Foo =
    let bar() =
        Logger.Trace("hello")

// hello from c:\path\to\Logging.fsx.bar on line 32
Foo.bar()

MS Docs还有更多关于Caller Information的话。

请注意,当您从 fsi 中运行此代码时,行号仅在第一次时才是准确的。

答案 2 :(得分:3)

可能不是您想要的,但是您可以使用报价将其提取:

let x<'T> : 'T = Unchecked.defaultof<'T>
<@ AddTwoNums x x x@> 
|> function Microsoft.FSharp.Quotations.Patterns.Call(_,mi,_)-> mi.Name | _ -> "unknown"

我猜您想看看如何获​​取堆栈跟踪:https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.stacktrace?view=netframework-4.7.2