可变参考细胞不适用于静态成员

时间:2014-11-24 18:25:59

标签: f# ref

我想我明白这里发生了什么,但我真的很感激有人说明为什么会这样。

基本上(我在这里动态编码,给出最简单的插图)我有一个类S,除其他外,它维护一个符号表,将名称与类的对象相关联。所以,我在课堂上定义:

static member (names:Map<string,S> ref) = ref Map.empty

...然后,在S中,我定义:

member this.addSymbol (table: Map<string,S> ref) (name:string)
    table := (!table).Add(name, this)
    this

我从其他地方打电话,因此:

ignore (s.addSymbol S.names "newname")
...

在S.addSymbol中,我可以看到新的键/值被添加到表中,但是S.names没有更新。相反,如果我像这样调用addSymbol:

ignore (
    let tmp = S.names
    s.addSymbol tmp "newName"
)

然后我可以看到tmp正在使用新的键/值对进行更新,但是S.names仍然没有更新。

这就是我认为我所知道的:ref机制没有任何问题 - 它在函数内外正确更新了指示对象。但是静态成员似乎有些奇怪 - 它就像是,而不是每次都给我相同的静态参考,它会复制&#39; a然后为此创建一个新的参考。

我做对了吗?如果是这样,为什么要这样做呢?我能做些什么才能让它表现得更明智?

提前致谢。

1 个答案:

答案 0 :(得分:6)

静态成员names属性,而不是静态值。它在内部是一个函数,每次调用它时都会创建一个新的映射。

如果值必须在类中,请使用static let在类中定义它。这样,参考单元只创建一次。

type S () =
    static let names = ref Map.empty : Map<string, S> ref
    static member Names = names

S.Names := ["Don", S()] |> Map.ofList
S.Names // gives map with one element

通常,类不会公开引用单元本身,而是公开读取,添加或删除名称的成员。然后,Names将返回!names - 或者名称将是普通的可变而不是引用单元格 - 而其他方法(如AddName)将改变该值。如果不需要这样的封装,请参阅kvb的评论,了解如何将此示例缩短为仅一个成员。 (感谢提示!)


替代方案:通常情况下,这是使用F#模块的情况。只需定义

let names = ref Map.empty : Map<string, S> ref

let mutable names = Map.empty : Map<string, S>

在模块中,根据需要具有附加访问限制或封装。