我正在尝试在F#程序集中构建一个log4net样式的界面。关键属性是公开一个返回对象实例的静态方法。 log4net利用C#委托来完成任务,例如与LogManager.GetLogger("log123")
。根据我的理解,代表们对于面向内部的F#库而言,并不像函数一样受到青睐。
下面简化的代码实现了目标,但是我对使用F#引用单元来保存实例化对象的映射感到不舒服。我对有关我的不适是否合理的反馈很感兴趣。
namespace Test
[<Interface>]
type IMyIface =
abstract member Addi : int -> int
[<Sealed>]
type TheMainObject internal (x:int) =
let mutable sum = x
interface IMyIface with
member this.Addi(y:int) = sum <- sum + y; sum
module internal Wrapr =
let mymap = ref Map.empty
let mgr s =
let m = !mymap
if Map.containsKey s m then m.[s]
else
let x = new TheMainObject(0)
mymap := m.Add(s, x)
x
[<Sealed>]
type Mgr() =
static member Get(n:string) =
Wrapr.mgr n :> IMyIface
Program.fs按如下方式调用上面的库:
open Test
let a = Mgr.Get("hello")
printfn "%d" (a.Addi(1))
let c = Mgr.Get("hello")
printfn "%d, %A" (c.Addi(3)) (a = c) //prints 4, true
提前感谢您的评论。
答案 0 :(得分:1)
可以在内部使用reference cell来保存可变值。您也可以使用.Net Dictionary代替地图。这是我在构建Mini IoC Container时采用的方法。如果您希望从多个线程调用访问引用单元格的函数,那么您应该使用lock或其他thread synchronization。
有多种方法可以公开Get方法。如果您希望重载方法,那么您采用的静态成员方法很有用。在这种情况下,您可以考虑在单独的模块上使用static let作为静态本地:
type [<Sealed>] Mgr() =
static let sync = obj()
static let lookup = Dictionary()
static let obtain name =
match lookup.TryGetValue(name) with
| true, x -> x
| false,_ ->
let x = TheMainObject(0)
lookup.Add(name,x)
x
static member Get(name:string) =
lock sync (fun () -> obtain name :> IMyIface)
如果您不希望重载Get函数,那么您可以使用模块:
module Mgr =
let private lookup = ref Map.empty
let private obtain name =
match (!lookup).TryFind name with
| Some(x) -> x
| None ->
let x = TheMainObject(0)
lookup := (!lookup).Add(name,x)
x
let Get name = obtain name :> IMyIface