[英]F# Record vs Class
我曾經把Record
視為(不可變)數據的容器,直到我遇到一些有啟發性的閱讀。
鑒於函數可以被視為F#中的值,記錄字段也可以保存函數值。 這提供了狀態封裝的可能性。
module RecordFun =
type CounterRecord = {GetState : unit -> int ; Increment : unit -> unit}
// Constructor
let makeRecord() =
let count = ref 0
{GetState = (fun () -> !count) ; Increment = (fun () -> incr count)}
module ClassFun =
// Equivalent
type CounterClass() =
let count = ref 0
member x.GetState() = !count
member x.Increment() = incr count
用法
counter.GetState()
counter.Increment()
counter.GetState()
看來,除了繼承之外,你無法用一個Class
來做,你無法用一個Record
和一個幫助函數。 其功能概念更好 ,例如模式匹配,類型推斷,高階函數,通用等式......
進一步分析, Record
可以看作是makeRecord()
構造函數實現的接口 。 應用(排序)關注點分離,其中makeRecord
函數中的邏輯可以更改而沒有違反合同的風險,即記錄字段。
將makeRecord
函數替換為與類型名稱匹配的模塊(ref聖誕樹記錄 )時,這種分離變得明顯。
module RecordFun =
type CounterRecord = {GetState : unit -> int ; Increment : unit -> unit}
// Module showing allowed operations
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module CounterRecord =
let private count = ref 0
let create () =
{GetState = (fun () -> !count) ; Increment = (fun () -> incr count)}
問:是否應將記錄視為數據的簡單容器,或者狀態封裝是否有意義? 我們應該在哪里繪制線,何時應該使用Class
而不是Record
?
請注意,鏈接帖子中的模型是純粹的,而上面的代碼則不是。
我不認為這個問題有一個普遍的答案。 記錄和類在某些潛在用途中重疊是肯定的,您可以選擇其中任何一種。
值得記住的一個區別是編譯器會自動為記錄生成結構相等和結構比較,這是您無法免費獲得的類。 這就是為什么記錄是“數據類型”的明顯選擇。
在記錄和類之間進行選擇時,我傾向於遵循的規則是:
{ ... with ... }
語法創建克隆,請使用記錄。 如果您正在編寫一些遞歸處理並需要保持狀態,這一點特別好。 我不認為每個人都會同意這一點,並不是涵蓋所有選擇 - 但一般來說,使用數據記錄和本地類型以及其他類似乎是在兩者之間進行選擇的合理方法。
如果你想在記錄中實現數據隱藏,我覺得有更好的方法來實現它,比如抽象數據類型 “模式”。
看看這個:
type CounterRecord =
private {
mutable count : int
}
member this.Count = this.count
member this.Increment() = this.count <- this.count + 1
static member Make() = { count = 0 }
Make
成員, count
領域是可變的 - 不是值得驕傲的事情,但我會說你公平的比賽例子。 此外,由於私有修飾符,它無法從模塊外部訪問。 要從外部訪問它,您具有只讀Count
屬性。 Increment
函數可以改變內部狀態。 CounterRecord
實例 - 正如Tomas所提到的,記錄的賣點。 至於記錄作為接口,你可能會看到有時 在現場 ,雖然我認為它更像是一個JavaScript / Haskell成語。 與那些語言不同,F#具有.NET的接口系統,與對象表達式結合使用時更加強大。 我覺得沒有太多理由為此重新調整記錄。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.