匿名記錄是由具名值組成的簡單匯總,這些值在使用之前不需要事先宣告。 您可以將它們宣告為結構或參考型別。 它們預設為參考型別。
語法
下列範例示範匿名記錄語法。 分隔為 [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
下列範例擴展了上一個範例,使用一個接受匿名記錄作為輸入的函數:
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 關鍵詞。 在此範例中,您會在呼叫struct時省略printCircleStats關鍵詞。
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 |}
匿名記錄的屬性
匿名記錄有許多特性,這些特性對於充分瞭解其使用方式至關重要。
匿名資料使用結構相等性和比較
與記錄類型一樣,匿名記錄在結構上是相等且可比較的。 只有當所有組成類型都支援相等和比較時,才會如此,就像記錄類型一樣。 若要支援相等或比較,兩個匿名記錄必須具有相同的「圖形」。
{| 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 需要使用傳入匿名類型。 匿名記錄是用來處理它們的工具。
局限性
匿名記錄的使用方式有一些限制。 有些是其設計固有的,但有些是可以改變的。
模式比對的限制
匿名記錄不支援模式比對,與具名記錄不同。 有三個原因:
- 不同於具名記錄類型,模式必須考慮匿名記錄的每個欄位。 這是因為匿名記錄不支援結構子類型,它們需要精確的欄位比對。
- 由於 (1),因此無法在模式比對表達式中具有其他模式,因為每個相異模式都表示不同的匿名記錄類型。
- 由於 (2),任何匿名記錄模式都會比使用「點」表示法更詳細。
有一個開放語言建議, 允許在有限的內容中進行模式比對。
可變性的限制
目前無法定義具有 mutable 數據的匿名記錄。 有一個 開放語言建議 可允許可變的數據。
結構匿名記錄的限制
無法將結構匿名記錄宣告為 IsByRefLike 或 IsReadOnly。 對於 和 匿名記錄,有IsByRefLikeIsReadOnly。