簡體   English   中英

F#記錄與班級

[英]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

請注意,鏈接帖子中的模型是純粹的,而上面的代碼則不是。

我不認為這個問題有一個普遍的答案。 記錄和類在某些潛在用途中重疊是肯定的,您可以選擇其中任何一種。

值得記住的一個區別是編譯器會自動為記錄生成結構相等和結構比較,這是您無法免費獲得的類。 這就是為什么記錄是“數據類型”的明顯選擇。

在記錄和類之間進行選擇時,我傾向於遵循的規則是:

  • 使用數據類型的記錄(免費獲得結構相等)
  • 當我想提供C#友好或.NET風格的公共API時(例如使用可選參數),請使用類。 您也可以使用記錄執行此操作,但我發現類更直接
  • 使用本地使用的類型的記錄 - 我認為你經常最終直接使用記錄(例如創建它們),因此添加/刪除字段是更多的工作。 對於僅在單個文件中使用的記錄,這不是問題。
  • 如果我需要使用{ ... 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM