我想我明白这里发生了什么,但我真的很感激有人说明为什么会这样。
基本上(我在这里动态编码,给出最简单的插图)我有一个类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然后为此创建一个新的参考。
我做对了吗?如果是这样,为什么要这样做呢?我能做些什么才能让它表现得更明智?
提前致谢。
答案 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>
在模块中,根据需要具有附加访问限制或封装。