如果我有一个函数是模块的一部分,并且我希望在函数内部有一个日志条目,则必须手动打印函数名称空间和名称,例如
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”是什么,即函数名称?
答案 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