【问题标题】:F# properties designF# 属性设计
【发布时间】:2023-03-18 13:28:01
【问题描述】:

因此,据我所知,当我在我的 F# 代码中创建一些属性时,因为 F# 不支持自动属性。我必须创建支持字段并将它们初始化为 null,这在函数式编程术语中似乎不正确。例如

let mutable albums : DbSet = null let mutable genres : DbSet = null member x.Albums with get() = albums and set(value) = albums <- value member x.Genres with get() = genres and set (value) = genres <- value

有没有更好的方法来做到这一点?非常感谢您的建议。

【问题讨论】:

  • 一件事——除了 Pad 和 TomasPetricek 的出色答案:如果你真的想成为“功能性”,那么我会首先检查你需要类或类结构的假设地方。自动属性通过将给定类的内部暴露给应用程序的其余部分来破坏良好的信息隐藏。如果您需要暴露给应用程序其余部分的类的内部,那么您可能一开始就不需要该类。我想我的意思是,如果你真的想从功能上摆脱一切都必须在类中的想法。
  • 哦,还有一件事——鉴于您正在从事的工作,您可能会发现这篇博客文章很有帮助:bugsquash.blogspot.com/2011/11/lenses-in-f.html

标签: f#


【解决方案1】:

除非您正在做一些复杂的事情,否则我建议您使用记录而不是类。基本上,它们是具有额外特性的类:不变性、结构平等、模式匹配等:

type Playlists = {
    Albums: DbSet;
    Genres: DbSet
    }

您可以轻松获取记录的字段:

let p = {Albums = ...; Genres = ...}
let albums = p.Albums
let genres = p.Genres

在默认记录中,字段是不可变的;您可以在记录中声明可变字段,但这被认为是一种不好的做法。虽然您无法设置属性,但您可以从旧记录创建新记录。默认不变性通常不是问题,而且它使代码更实用,更容易推理:

   let p = {Albums = a; Genres = g}

   // Create new records by updating one field
   let p1 = {p with Albums = a1} 
   let p2 = {p with Genres = g2} 

如果你坚持要创建类,建议使用带显式参数的构造函数:

type Playlists(a: DbSet, g: DbSet) =
     let mutable albums = a
     let mutable genres = g
     // ...

当需要默认构造函数时,您可以将Unchecked.default&lt;'T&gt; 用于不可为空的字段,或者更好地使用它们的默认构造函数:

 // Set fields using dump values
 let mutable albums = new DbSet()
 let mutable genres = new DbSet()

但请确保在实际使用它们之前设置这些字段。

【讨论】:

  • 请注意,您还可以通过简单的方式使记录的字段可变。记录也有一些缺点,例如,您必须显式设置其所有字段的值才能构造一个对象。另一个缺点是记录类型不能继承也不能派生。
  • 记录确实可以有可变字段。但我认为这是一个糟糕的设计。当你走这条路时,应该首选课程。
  • 感谢 Pad 的建议。也许我会使用记录,但就我而言,我认为我对房产更感兴趣。但是我会重新考虑您的建议,看看我是否可以将我的类型设计为记录。
  • @pad:我同意这是一个不好的做法,我刚刚提到它是因为问题已经包含可变字段,所以我认为这将是相关信息。
  • @ShdNx:同意,我更新了提到可变记录字段的答案。
【解决方案2】:

仅供参考 - 自动属性计划用于 F# 3.0。请参阅preview documentation [MSDN]。看起来你的例子会变成:

type Music() =
  member val Albums : DbSet = null with get, set
  member val Genres : DbSet = null with get, set

【讨论】:

  • 显然这些东西是不允许私有的
  • member val GroupBox1:GroupBox = new GroupBox() throws 说Additional information: The initialization of an object or value resulted in an object or value being accessed recursively before it was fully initialized. 这个构造函数似乎在持有自动属性构造函数的类之后运行。
  • 确定是:member val private ...
【解决方案3】:

当您需要 mutable 属性时,F# 不支持自动属性,但当您只需要 readonly 属性时,它支持轻量级语法。如果您正在编写一些函数式代码,那么使用 readonly 属性实际上可能更合适:

type Music(genres : DbSet, albums : DbSet) = 
  member x.Albums = albums
  member x.Genres = genres

这与 pad 建议的记录基本相同,但如果您想更好地控制类型的外观(以及它们在 C# 中的显示方式,或用于数据绑定),它可能更合适。

如果DbSet 是可变类型,那么您可能只需使用上述类型并将其初始化一次(您仍然可以修改DbSet 值)。如果要更改DbSet 的值,可以添加一个返回克隆对象的方法:

  member x.WithAlbums(newAlbums) = 
    Music(genres, newAlbums)

在 F# 中使用 nullUnchecked.defaultOf&lt;_&gt; 被认为是一种非常糟糕的做法,您应该始终尝试创建完全初始化的对象。如果值可能丢失,您可以使用option 类型来表示,但是您必须始终为丢失值编写处理程序,以确保您的程序安全。

【讨论】:

  • 我认为使用 null 不能被认为是一种很好的实践,这种语言具有明确的 Option 类型,仅用于此目的。感谢您的建议。
  • WPF 是否接受选项类型以支持显式空值?
猜你喜欢
  • 2010-10-18
  • 2010-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-05
  • 2015-05-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多