F# - “自填充”类型属性

本文关键字:类型 属性 自填充 填充 | 更新日期: 2023-09-27 18:37:01

在C#中,我可以有这个:

public class A {
    ...
    private List<int> _items;
    public List<int> Items{
        get {
            if (_items == null){
                _items = DAL.FetchFromDB();
            }
            return _items;
        }
    }
}

这样,我可以初始化类 A,当我请求项目时

  1. 我保证无需明确填写列表即可获得它。
  2. 我避免重复调用数据库

F# 的等效构造是什么?我不确定如何制作一个执行此操作的类型...

我不一定要求可变列表;我想知道在 F# 中做这样的事情的标准形式是什么,因为我只是在学习语言。

F# - “自填充”类型属性

正如ildjarn在上面的评论中指出的那样,你可以以等效的方式使用lazy关键字:

type A () =
    let items = lazy (DAL.FetchFromDB ())
    member this.Items with get () = items.Value

但是,问题是它是"惯用的"F#,还是一般的"良好做法"?

Items属性在引用上不透明(因为它不是确定性的),函数式编程往往非常强调引用透明度。

当然:F#不是一种纯粹的函数式语言;它是一种函数式第一语言。尽管如此,这意味着要充分利用它,您应该在设计中采用类似的功能优先方法。

你自己的代码越实用,你从 F# 中获得的价值就越大。

F# 代码的命令性、隐式或面向对象越多,从中获得的收益就越少。

因此,尝试将 C# 逐字转换为 F# 代码并不总是有意义的。你可以,但你会从中获得什么吗?

底线是可以将 C# 转换为类似于 F# 中的内容,但您应该考虑这是否是一个好主意。使用 lazy 关键字本身并没有错,但隐含性是我会重新考虑的。

这是对 F# 的一对一翻译:

type A() =
    let mutable _items = null
    member this.items 
        with get() = 
            if _items = null then _items <- DAL.FetchFromDB()
            _items
let a = A()
let x = a.items  // db access here
let y = a.items

有更好(和更短)的方法可以使用对象在 F# 中编写等效代码(请参阅另一个答案),但您根本不需要创建对象,您可以简单地定义一个函数,正如其他人已经指出的那样,您可以使用 lazy 关键字:

let items = 
    let v = lazy DAL.FetchFromDB()
    fun () -> v.Value
let x = items()  // db access here
let y = items()

或者直接使用标准的惰性值,我更喜欢这样,因为您在items类型中明确表示您的值是惰性计算的,而不是将其隐藏在魔术对象属性或函数后面:

let items = lazy DAL.FetchFromDB()
let x = items.Value  // db access here
let y = items.Value

所以现在items的类型是Lazy<'List<'T>>它总是告诉你值将是延迟计算的,除此之外,在这种情况下,代码实际上更短。