共用方式為


匿名記錄

匿名記錄是不需要在使用之前就宣告的具名值簡單彙總。 您可以將其宣告為結構或參考型別。 其預設為參考型別。

語法

下列範例示範匿名記錄語法。 分隔為 [item] 的項目是選擇性的。

// Construct an anonymous record
let value-name = [struct] {| Label1: Type1; Label2: Type2; ...|}

// Use an anonymous record as a type parameter
let value-name = Type-Name<[struct] {| Label1: Type1; Label2: Type2; ...|}>

// Define a parameter with an anonymous record as input
let function-name (arg-name: [struct] {| Label1: Type1; Label2: Type2; ...|}) ...

基本使用方式

最好將匿名記錄視為不需要在具現化之前宣告的 F# 記錄類型。

例如,您可以在這裡與產生匿名記錄的函式互動方式:

open System

let getCircleStats radius =
    let d = radius * 2.0
    let a = Math.PI * (radius ** 2.0)
    let c = 2.0 * Math.PI * radius

    {| Diameter = d; Area = a; Circumference = c |}

let r = 2.0
let stats = getCircleStats r
printfn "Circle with radius: %f has diameter %f, area %f, and circumference %f"
    r stats.Diameter stats.Area stats.Circumference

下列範例會使用以匿名記錄做為輸入的 printCircleStats 函式,展開前一個範例:

open System

let getCircleStats radius =
    let d = radius * 2.0
    let a = Math.PI * (radius ** 2.0)
    let c = 2.0 * Math.PI * radius

    {| Diameter = d; Area = a; Circumference = c |}

let printCircleStats r (stats: {| Area: float; Circumference: float; Diameter: float |}) =
    printfn "Circle with radius: %f has diameter %f, area %f, and circumference %f"
        r stats.Diameter stats.Area stats.Circumference

let r = 2.0
let stats = getCircleStats r
printCircleStats r stats

使用與輸入類型沒有相同「圖形」的任何匿名記錄類型來呼叫 printCircleStats,將會無法編譯:

printCircleStats r {| Diameter = 2.0; Area = 4.0; MyCircumference = 12.566371 |}
// Two anonymous record types have mismatched sets of field names
// '["Area"; "Circumference"; "Diameter"]' and '["Area"; "Diameter"; "MyCircumference"]'

結構匿名記錄

匿名記錄也可以定義為具有選擇性 struct 索引鍵的結構。 下列範例會產生及取用結構匿名記錄來增強前一個範例:

open System

let getCircleStats radius =
    let d = radius * 2.0
    let a = Math.PI * (radius ** 2.0)
    let c = 2.0 * Math.PI * radius

    // Note that the keyword comes before the '{| |}' brace pair
    struct {| Area = a; Circumference = c; Diameter = d |}

// the 'struct' keyword also comes before the '{| |}' brace pair when declaring the parameter type
let printCircleStats r (stats: struct {| Area: float; Circumference: float; Diameter: float |}) =
    printfn "Circle with radius: %f has diameter %f, area %f, and circumference %f"
        r stats.Diameter stats.Area stats.Circumference

let r = 2.0
let stats = getCircleStats r
printCircleStats r stats

結構推斷

結構匿名記錄也允許在呼叫位置指定 struct 索引鍵的「結構推斷」。 在此範例中,您會在呼叫 printCircleStats 時滑動 struct 索引鍵:


let printCircleStats r (stats: struct {| Area: float; Circumference: float; Diameter: float |}) =
    printfn "Circle with radius: %f has diameter %f, area %f, and circumference %f"
        r stats.Diameter stats.Area stats.Circumference

printCircleStats r {| Area = 4.0; Circumference = 12.6; Diameter = 12.6 |}

反向模式 (當輸入類型不是結構匿名記錄時指定 struct) 將無法編譯。

在其他類型中內嵌匿名記錄

宣告案例為記錄的區分等位相當實用。 但是,如果記錄中的資料與區分等位的類型相同,您就必須將所有類型定義為相互遞迴。 使用匿名記錄可避免此限制。 以下是範例類型和模式與其相符的函式:

type FullName = { FirstName: string; LastName: string }

// Note that using a named record for Manager and Executive would require mutually recursive definitions.
type Employee =
    | Engineer of FullName
    | Manager of {| Name: FullName; Reports: Employee list |}
    | Executive of {| Name: FullName; Reports: Employee list; Assistant: Employee |}

let getFirstName e =
    match e with
    | Engineer fullName -> fullName.FirstName
    | Manager m -> m.Name.FirstName
    | Executive ex -> ex.Name.FirstName

複製及更新運算式

匿名記錄支援使用複製及更新運算式進行建構。 例如,以下是建構匿名記錄的新執行個體來複製現有記錄資料的方式:

let data = {| X = 1; Y = 2 |}
let data' = {| data with Y = 3 |}

不過,與具名記錄不同,匿名記錄可讓您使用複製及更新運算式來建構完全不同的形式。 下列範例會採用上一個範例中相同的匿名記錄,並將其展開至新的匿名記錄:

let data = {| X = 1; Y = 2 |}
let expandedData = {| data with Z = 3 |} // Gives {| X=1; Y=2; Z=3 |}

也可以從具名記錄的執行個體建構匿名記錄:

type R = { X: int }
let data = { X = 1 }
let data' = {| data with Y = 2 |} // Gives {| X=1; Y=2 |}

您也可以在參考與結構匿名記錄之間來回複製資料:

// Copy data from a reference record into a struct anonymous record
type R1 = { X: int }
let r1 = { X = 1 }

let data1 = struct {| r1 with Y = 1 |}

// Copy data from a struct record into a reference anonymous record
[<Struct>]
type R2 = { X: int }
let r2 = { X = 1 }

let data2 = {| r1 with Y = 1 |}

// Copy the reference anonymous record data into a struct anonymous record
let data3 = struct {| data2 with Z = r2.X |}

匿名記錄的屬性

匿名記錄有一些特性,對於充分了解其使用方式至關重要。

匿名記錄為名義性的

匿名記錄為名義性的類型。 最好將其視為不需要預先宣告的具名記錄型別 (這也是名義性的)。

請考慮使用下列兩個匿名記錄宣告的範例:

let x = {| X = 1 |}
let y = {| Y = 1 |}

xy 值具有不同的類型,且彼此並不相容。 這兩個值不相等,且無法比較。 為說明這一點,請考慮具名記錄對等項目:

type X = { X: int }
type Y = { Y: int }

let x = { X = 1 }
let y = { Y = 1 }

在考量類型對等或比較時,相較於具名記錄對等項目,匿名記錄原本就沒有任何不同之處。

匿名記錄會使用結構對等和比較

如同記錄類型,匿名記錄在結構上對等且可比較的。 僅在所有組成類型都支援對等和比較 (例如記錄類型) 時,才適用這種情況。 若要支援對等或比較,兩個匿名記錄必須具有相同的「圖形」。

{| a = 1+1 |} = {| a = 2 |} // true
{| a = 1+1 |} > {| a = 1 |} // true

// error FS0001: Two anonymous record types have mismatched sets of field names '["a"]' and '["a"; "b"]'
{| a = 1 + 1 |} = {| a = 2;  b = 1|}

匿名記錄為可序列化的

您可以序列化匿名記錄,就如同使用具名記錄一樣。 以下是使用 Newtonsoft.Json 的範例:

open Newtonsoft.Json

let phillip' = {| name="Phillip"; age=28 |}
let philStr = JsonConvert.SerializeObject(phillip')

let phillip = JsonConvert.DeserializeObject<{|name: string; age: int|}>(philStr)
printfn $"Name: {phillip.name} Age: %d{phillip.age}"

匿名記錄適用於透過網路傳送輕量型資料,而不需要預先定義序列化/還原序列化類型的網域。

匿名記錄會與 C# 匿名型別交互操作

您可以使用需要使用 C# 匿名型別的 .NET API。 C# 匿名型別使用匿名記錄即可簡單進行交互操作。 下列範例示範如何使用匿名記錄來呼叫需要匿名型別的 LINQ 多載:

open System.Linq

let names = [ "Ana"; "Felipe"; "Emilia"]
let nameGrouping = names.Select(fun n -> {| Name = n; FirstLetter = n[0] |})
for ng in nameGrouping do
    printfn $"{ng.Name} has first letter {ng.FirstLetter}"

.NET 中有許多其他 API 需要使用傳入匿名型別。 匿名記錄是您使用這些 API 的工具。

限制

匿名記錄的使用方式會有一些限制。 有些是其設計固有的,而有些則是可變更的。

模式比對的限制

不同於具名記錄,匿名記錄並不支援模式比對。 有三個理由:

  1. 不同於具名記錄類型,模式必須考慮匿名記錄的每個欄位。 這是因為匿名記錄不支援結構化子類型 – 其為名義類型。
  2. 由於 (1),無法在模式比對運算式中擁有額外的模式,因為每個相異模式都代表不同的匿名記錄類型。
  3. 由於 (2),任何匿名記錄模式都會比使用「點」標記法更為詳盡。

開放語言建議,可在有限的內容中允許模式比對

可變動性的限制

目前無法定義包含 mutable 資料的匿名記錄。 開放語言建議可允許可變動的資料。

結構匿名記錄的限制

無法將結構匿名記錄宣告為 IsByRefLikeIsReadOnlyIsByRefLikeIsReadOnly 匿名記錄都有開放語言建議