背景:我正在尝试扩展我现有的日志框架,它现在是printfn
和朋友之间的静态非线程安全方法的包装器。我的设计目标是:拥有通用接口ILog
和具体类,如Dbg
,Log
,Trace
。遗憾的是,我不能使用模块和函数,因为我喜欢在方法上使用ConditionalAttribute
。
我的工作方法看起来像这样:
type ILog<'T, 'U, 'V> =
abstract member log: 'T -> unit
abstract member log: 'U * 'V -> unit
type Dbg<'T, 'U, 'V when 'T :> Printf.StringFormat<string> and 'U :> Printf.StringFormat<'V -> string>>() =
static member inline _do_other_stuff() = "other stuff"
static member inline private _helper() =
printfn "%s %s" (Dbg<_,_,_>._do_other_stuff())
interface ILog<'T, 'U, 'V> with
[<Conditional("DEBUG")>] // removed from call site
member this.log(msg) = sprintf msg |> Dbg<_,_,_>._helper()
[<Conditional("DEBUG")>] // removed from call site
member this.log(fmt, a) = sprintf fmt a |> Dbg<_,_,_>._helper()
module TestMe =
let test() =
let dbg = new Dbg<_,_,_>() :> ILog<_,_,_>
dbg.log("test%i", 2)
这里,编译器和语法检查器和着色器正确检测到dbg.log("test%i", 2)
行,这正是我想要的。如果我改为编写"test%s"
,它也会正确地引发错误。
现在,如果我采用上述方法并对其进行扩展以创建ILog.log
方法的更多重载,由于所有类型注释和所需的语法使用Dbg<_,_,_>
,这会很快变得非常毛茸茸。 }。其中一些可以隐藏起来,但我仍然认为必须有更好的方法。
我尝试过的一种方法,看起来很FSharpish:
type ILog =
abstract member log: _ -> unit
abstract member log: _ * _ -> unit
这会编译界面并分别将类型推断为'a0
和'a0 -> 'a1
(这似乎不对,为什么第二个log
成员获得相同的'a0
?) 。但是,我找不到任何实现这种过于通用的界面的方法:
type Dbg() =
interface ILog with
member this.log v = v + 1 |> ignore // it won't infer int here
提出:
声明的类型参数&#39;?&#39;这里不能使用,因为在编译时无法解析类型参数。
F#4.0是否有一种以更通用的方式声明接口的方式,或者我不得不专门声明它们(虽然这有效,但是很乏味)。