Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Анонимные записи — это простые агрегаты именованных значений, которые не нужно объявлять перед использованием. Их можно объявить как структуры или ссылочные типы. По умолчанию они являются ссылочными типами.
Синтаксис
В следующих примерах показан синтаксис анонимной записи. Элементы, разделенные как [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 в месте вызова. В этом примере вы опускаете ключевое слово 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#
Можно использовать API .NET, требующий использования анонимных типов C#. Анонимные типы 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}"
Существует множество других API, используемых в .NET, для которых требуется передача анонимного типа. Анонимные записи — это средство для работы с ними.
Ограничения
Анонимные записи имеют некоторые ограничения в их использовании. Некоторые из них присущи их дизайну, но другие можно изменить.
Ограничения при сопоставлении шаблонов
Анонимные записи не поддерживают сопоставление шаблонов, в отличие от именованных записей. Существует три причины:
- Шаблон должен учитывать каждое поле анонимной записи, в отличие от именованных типов записей. Это связано с тем, что анонимные записи не поддерживают структурную подтипию— они требуют точного сопоставления полей.
- Из-за (1) нет возможности иметь дополнительные шаблоны в выражении сопоставления шаблонов, так как каждый отдельный шаблон подразумевает другой анонимный тип записи.
- Из-за (2) любой анонимный шаблон записи будет более многословным, чем использование «точечной» нотации.
Существует открытое предложение языка для разрешения сопоставления шаблонов в ограниченных контекстах.
Ограничения с изменяемостью
В настоящее время невозможно определить анонимную запись с mutable данными. Существует открытое предложение по языку, позволяющее изменять данные.
Ограничения структур с анонимными записями
Невозможно объявить анонимные записи структуры как IsByRefLike или IsReadOnly. Существует предложение по открытому языку для IsByRefLike и IsReadOnly анонимных записей.