Поделиться через


Записи (F#)

Записи представляют простые агрегаты именованных значений, дополнительно с членами.

[ attributes ]
type [accessibility-modifier] typename = { 
    [ mutable ] label1 : type1;
    [ mutable ] label2 : type2;
    ...
    }
    member-list

Заметки

В предыдущем синтаксисе typename — имя типа записи; label1 и label2 — имена значений, называемые метками; type1 и type2 — типы этих значений.member-list — необязательный список членов для типа.

Ниже приведены некоторые примеры.

type Point = { x : float; y: float; z: float; }
type Customer = { First : string; Last: string; SSN: uint32; AccountNumber : uint32; }

Когда каждая метка находится на отдельной строке, точка с запятой необязательна.

Значения можно задать в выражениях, называемых выражениями записей.Компилятор определяет тип по использованным меткам (если метки в достаточной степени отличаются от меток других типов записи).Выражение записи заключено в фигурные скобки ({ }).В следующем коде показано выражение записи, инициализирующее запись с тремя элементами с плавающей запятой с метками x, y и z.

let mypoint = { x = 1.0; y = 1.0; z = -1.0; }

Не используйте сокращенную форму, если может существовать другой тип, имеющий такие же метки.

type Point = { x : float; y: float; z: float; }
type Point3D = { x: float; y: float; z: float }
// Ambiguity: Point or Point3D?
let mypoint3D = { x = 1.0; y = 1.0; z = 0.0; }

Метки последнего объявленного типа имеют приоритет над метками ранее объявленного типа, поэтому в предыдущем примере mypoint3D определяется как Point3D.Можно явно указать тип записи, как в следующем коде.

let myPoint1 = { Point.x = 1.0; y = 1.0; z = 0.0; }

Методы могут определяться как для типов записей, так и для типов классов.

Создание записей с помощью выражений записей

Записи можно инициализировать, используя метки, которые определяются в записи.Выражение, обеспечивающее такую инициализацию, называется выражением записи.Используйте скобки для обрамления выражения записи и точку с запятой в качестве разделителя.

В следующем примере показано, как создать запись.

type MyRecord = {
    X: int;
    Y: int;
    Z: int 
    }

let myRecord1 = { X = 1; Y = 2; Z = 3; }

Точки с запятой после последнего поля выражения записи и в определении типа являются необязательными, независимо от того, расположены ли все поля в одной строке.

При создании записи необходимо задать значения для каждого поля.Для любого поля нельзя ссылаться на значения других полей в выражении инициализации.

В следующем коде тип записи myRecord2 определяется на основе имен полей.Дополнительно можно задать имя типа явно.

let myRecord2 = { MyRecord.X = 1; MyRecord.Y = 2; MyRecord.Z = 3 }

Если требуется скопировать существующую запись и, возможно, изменить некоторые значения полей, удобно использовать другую форму конструкции записи.Это показано в следующей строке кода.

let myRecord3 = { myRecord2 with Y = 100; Z = 2 }

Эта форма выражения записи называется формой копирования и обновления выражения записи.

По умолчанию записи являются неизменяемыми; однако можно легко создать измененные записи с помощью операции копирования и обновить выражение.Можно также в явном виде задать изменяемое поле.

type Car = {
    Make : string
    Model : string
    mutable Odometer : int
    }
let myCar = { Make = "Fabrikam"; Model = "Coupe"; Odometer = 108112 }
myCar.Odometer <- myCar.Odometer + 21

Не используйте атрибута DefaultValue с полей записи.Лучшим подходом является определение экземпляров по умолчанию записи с полями, которые инициализируются значениями по умолчанию и использовать копию и обновить записи выражения для набора полей, которые отличаются от значений по умолчанию.

// Rather than use [<DefaultValue>], define a default record.
type MyRecord =
    { 
        field1 : int 
        field2 : int
    }

let defaultRecord1 = { field1 = 0; field2 = 0 }
let defaultRecord2 = { field1 = 1; field2 = 25 }

// Use the with keyword to populate only a few chosen fields
// and leave the rest with default values.
let rr3 = { defaultRecord1 with field2 = 42 }

Соответствие шаблону с записями

С записями можно использовать соответствие шаблону.Можно в явном виде задать некоторые поля, а для других полей указать переменные, значения которым будут присвоены при нахождении соответствия.Это показано в следующем примере кода.

type Point3D = { x: float; y: float; z: float }
let evaluatePoint (point: Point3D) =
    match point with
    | { x = 0.0; y = 0.0; z = 0.0 } -> printfn "Point is at the origin."
    | { x = xVal; y = 0.0; z = 0.0 } -> printfn "Point is on the x-axis. Value is %f." xVal
    | { x = 0.0; y = yVal; z = 0.0 } -> printfn "Point is on the y-axis. Value is %f." yVal
    | { x = 0.0; y = 0.0; z = zVal } -> printfn "Point is on the z-axis. Value is %f." zVal
    | { x = xVal; y = yVal; z = zVal } -> printfn "Point is at (%f, %f, %f)." xVal yVal zVal

evaluatePoint { x = 0.0; y = 0.0; z = 0.0 }
evaluatePoint { x = 100.0; y = 0.0; z = 0.0 }
evaluatePoint { x = 10.0; y = 0.0; z = -1.0 }

Результат выполнения этого кода следующий.

Point is at the origin.
Point is on the x-axis. Value is 100.000000.
Point is at (10.000000, 0.000000, -1.000000).

Различия между записями и классами

Поля записей отличаются от классов в том отношении, что они автоматически представляются как свойства и используются при создании и копировании записей.Построение записей также отличается от построения класса.В типе записи нельзя определить конструктор.Вместо этого применяется синтаксис конструкции, описанный в данной теме.Классы не имеют непосредственной связи между параметрами, полями и свойствами конструктора.

Как и типы объединений и структур, записи обладают семантикой структурного равенства.Классы обладают семантикой равенства ссылок.Это продемонстрировано в следующем примере.

type RecordTest = { X: int; Y: int }
let record1 = { X = 1; Y = 2 }
let record2 = { X = 1; Y = 2 }
if (record1 = record2) then
    printfn "The records are equal."
else
    printfn "The records are unequal."

Если написать одинаковый код с классами, два объекта класса не будут равными, так как два значения будут представлять два объекта в куче и будут сравниваться только адреса (если только тип класса не переопределил метод System.Object.Equals).

См. также

Ссылки

Классы (F#)

Сопоставление шаблонов (F#)

Другие ресурсы

Типы языка F#

Справочник по языку F#