成员与类的F#记录

时间:2019-04-24 21:36:47

标签: f#

在F#中,您可以使用成员函数定义记录,也可以创建类。

type Album = {
    Id: int
    Name: string
    DateReleased: DateTime
    Genre: Genre }
with
    member this.PrintMessage() =
        printf "Hello from %s\n" this.Name

如此使用

let first = 
        { Id = 1; 
          Name = "Hello World!"
          Genre = KPop;
          DateReleased = DateTime(1991, 8, 12) }
first.PrintMessage()

使用类可能就是这样。

type Album(
     Id: int, 
     Name: string, 
     DateReleased: DateTime, 
     Genre: Genre ) as me =
     do
         me.PrintMessage()
     member this.PrintMessage() =
         printf "Hello %s" Name

何时方便使用其中一个? 只能通过记录来完成域建模吗?

是否可以通过某些隐藏的FSharp Casting将类转换为记录,反之亦然?

3 个答案:

答案 0 :(得分:5)

可以同时使用记录和类来进行域建模。我认为何时使用其中一个主要取决于偏好和环境。我个人通常不使用类,但是我知道有些人经常使用它们。类更多地依赖于突变,并且在易于使用的代价下性能更高。我个人认为,如果要编写面向对象的样式,请使用类;如果要编写功能样式,请使用记录。记录模式匹配和解构更加容易。这种区别的一部分是文化上的,仅仅是因为您可以以特定的方式做某事,并不意味着其他人就能轻松地理解您。代码被编写为供您自己和他人阅读。当您选择记录或类时,它传达有关这些类型的样式和用法的意图。最后,我不知道一个类可以在F#中转换为记录,如果可以的话,我实际上仍然建议不要这样做。这是因为将记录用作类,将类用作记录与这些构造的文化规范相冲突。我个人认为您的示例成员适用于记录和类。我通常喜欢对与给定类型关联的函数使用同名的模块,因为当函数碰巧消耗除记录类型之外的其他东西时,它可以减少混乱。但是,我认为任何有风度的人都不会对那里的任何代码示例感到厌烦。

type Album(
    Id: int, 
    Name: string, 
    DateReleased: string, 
    Genre: int ) as me =
    let mutable genre = Genre  
    //readonly property
    member val Id = Id
    //autoproperty with get and set
    member val Name = Name with get, set
    member val DateReleased = DateReleased with get, set
    //property with explicit get and set
    member this.Genre 
        with get () = genre
        and set (value) = genre <- value

请参阅When should I use let, member val and member this.?

答案 1 :(得分:1)

有点麻烦的是,不仅仅是F#记录字段和CLI属性作为数据容器。两者在幕后是相同的:不可变F#记录字段的访问器实现为CLI属性获取器。

如果用例允许,还可以使用带有F#mutable val定义的CLI字段。另一方面,类似的只读val定义也由属性获取器公开。

type Album =
    // CLI property getter
    val Id : int
    // mutable CLI field
    [<DefaultValue>]
    val mutable Name : string
    // non-primary constructor with sequence construction (before)
    new (id: int, name : string) as me =
         { Id = id } then me.Name <- name

有缺点:

  • 缺少主要构造函数,
  • 额外的构造函数表达式的笨拙
  • 使用DefaultValueAttribute
  • 确保字段类型的默认初始化

它可能被认为是单项的。

答案 2 :(得分:0)

您也可以考虑使用与记录类型同名的模块,如下所示:

type Album = {
    Id: int
    Name: string
    DateReleased: DateTime
    Genre: Genre
}
module Album =
    let printMessage album =
        printfn "Hello from %s" album.Name

let first = {
    Id = 1
    Name = "Hello World!"
    Genre = KPop
    DateReleased = DateTime(1991, 8, 12)
}
Album.printMessage first