gpt4 book ai didi

f# - 使用循环引用设计和更好的选项对不可变类进行批判

转载 作者:行者123 更新时间:2023-12-04 13:34:26 24 4
gpt4 key购买 nike

我有一个工厂类,该类创建具有循环引用的对象。我也希望它们是不变的(从某种意义上来说)。因此,我使用了以下技术,并使用了各种闭包:

[<AbstractClass>]
type Parent() =
abstract Children : seq<Child>
and Child(parent) =
member __.Parent = parent

module Factory =

let makeParent() =
let children = ResizeArray()
let parent =
{ new Parent() with
member __.Children = Seq.readonly children }
[Child(parent); Child(parent); Child(parent)] |> children.AddRange
parent

internal AddChild方法相比,我更喜欢这种方法,因为它可以更好地保证不变性。也许是神经质的,但我更喜欢使用闭包进行访问控制。

这个设计有什么陷阱吗?是否有更好的方法(也许不太麻烦)来做到这一点?

最佳答案

即使创建抽象类的实例,也可以使用F#的递归初始化支持:

let makeParent() =
let rec children = seq [ Child(parent); Child(parent); Child(parent) ]
and parent =
{ new Parent() with
member __.Children = children }
parent

编译代码时,F#使用惰性值,因此值 children变为惰性值,并且属性 Children访问此惰性计算的值。很好,因为它可以先创建 Parent实例(引用惰性值),然后实际构造序列。

用记录做同样的事情不会很好地工作,因为不会延迟任何计算,但是在这里却能很好地工作,因为在创建 Parent时实际上并没有访问序列(如果是记录,这会是一个必须进行评估的字段)。

F#编译器通常无法告诉您这是否正确,因此它发出警告,可以使用 #nowarn "40"禁用该警告。

总的来说,我认为使用 let rec .. and ..初始化递归值是一件好事-有点受限制(其中一个引用必须延迟),但是它迫使您将递归引用保持隔离状态,并且,我认为它可以使您的递归引用保持隔离代码更简单。

编辑要在可能出现错误的情况下添加示例-如果 Child的构造函数尝试访问其父级的 Children集合,则在创建惰性值之前会强制对其求值,并且会出现运行时错误(这是警告说的是什么)。尝试将其添加到 Child的构造函数中:
do printfn "%d" (Seq.length parent.Children)

关于f# - 使用循环引用设计和更好的选项对不可变类进行批判,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7031982/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com