Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Чтобы предоставить общий доступ к полям без объединения различных типов моделей, используйте распространение записей, доступные начиная с F# 11.
Используется ...Type для внесения объявлений полей в новую именованную запись или ...expression для внесения значений полей в именованную или анонимную запись. Повторное использование объявления и сопоставление значений являются независимыми вариантами.
Создание типов записей
Чтобы предоставить общий доступ к полям подключения между типами конфигураций, перейдите Endpoint в новое объявление записи:
type Endpoint = { Host: string; Port: int }
type ClientOptions =
{ ...Endpoint
TimeoutSeconds: int }
ClientOptions имеет Host, Portи TimeoutSeconds. Это отдельный номинальный тип, а не Endpoint подтип.
Несколько источников и явных объявлений могут отображаться в определении одного типа:
type Logging = { LogLevel: string }
type LoggedClientOptions = { ...ClientOptions; ...Logging }
Участвуют только поля записи. Методы, свойства, которые не являются полями записи, и статические элементы не копируются. Источник может быть именованной записью или анонимным типом записи, включая сокращение анонимной записи. Назначение распространения типа должно быть именованным типом записи.
Создание значений записей
Скопируйте конечную точку в параметры клиента, а затем используйте анонимную запись для замены узла:
let local = { Host = "localhost"; Port = 5432 }
let options: ClientOptions = { ...local; TimeoutSeconds = 30 }
let remote = {| ...options; Host = "db.example.com" |}
printn $"{remote.Host}:{remote.Port}, timeout {remote.TimeoutSeconds}s"
// db.example.com:5432, timeout 30s
Распространение выражений принимает именованные или анонимные источники записей и назначения, включая записи структуры. Каждое целевое поле должно получить значение. Явная целевая заметка полезна, если несколько именованных типов записей совместно используют метки.
Укрепление типа поля
Последующее объявление поля может заменить тип поля распространения. Например, представляет идентификатор синтаксического анализа, а затем различает импортированную type CustomerId = CustomerId of System.Guidзапись от одной с этим идентификатором:
type ImportedCustomer = { Name: string; Id: string }
type IdentifiedCustomer = { ...ImportedCustomer; Id: CustomerId }
let identify (source: ImportedCustomer) =
match Guid.TryParse source.Id with
| true, id -> Ok ({ ...source; Id = CustomerId id }: IdentifiedCustomer)
| false, _ -> Error "Invalid customer identifier."
Guid.TryParse проверяет текст; распространяемые поля копируются только в полях.
CustomerId различает идентификаторы от других идентификаторов GUID без применения дополнительных правил.
Объединение независимых моделей
Создание объявлений из курированной номинальной записи и анонимного типа записей, а затем сбор значений из независимых источников:
type PublicProductFields = { Name: string; UnitPrice: decimal }
type ProductRow = { ...PublicProductFields; InternalCost: decimal }
type StockCounts = {| InStock: int; Reserved: int |}
type ProductCard =
{ ...PublicProductFields
...StockCounts
CanOrder: bool }
let product: ProductRow =
{ Name = "Notebook"; UnitPrice = 12.50m; InternalCost = 4m }
let stock: StockCounts = {| InStock = 8; Reserved = 2 |}
let card: ProductCard =
{ ...product; ...stock; CanOrder = stock.InStock > stock.Reserved }
let viewState = {| ...card; Selected = false |}
let selected = {| ...viewState; Selected = true |}
Именованные и анонимные источники предоставляют ProductCard; карточка затем предоставляет состояние анонимного представления.
CanOrder вычисляется один раз, а не пересчитывается при последующем изменении счетчиков.
Project в известный целевой объект
Целевой тип выбирает поля для сохранения. Несколько источников могут предоставлять замены:
let endpoint: Endpoint = { ...options }
let overrides = {| Host = "db.example.com"; Port = 5433 |}
let production: ClientOptions =
{ ...options; ...overrides; TimeoutSeconds = 60 }
printn $"{endpoint.Host}:{endpoint.Port}"
printn $"{production.Host}:{production.Port}, timeout {production.TimeoutSeconds}s"
// localhost:5432
// db.example.com:5433, timeout 60s
endpoint сохраняет только Host и Port; production объединяет источники и явные переопределения. Явные поля, отсутствующие в целевом объекте, остаются ошибками.
Неотметированная анонимная запись принимает все поля, внесенные в действие. Именованный целевой объект должен быть выводимым. Неизвестные источники нуждаются в собственных заметках, таких как (source: ClientOptions) параметр функции.
Порядок полей и переопределения
Вклады считываются слева направо. Для повторяющегося имени поля самый правый вклад выигрывает. Правило применяется как к объявлениям полей в типах, так и к значениям полей в выражениях.
let laterHost = {| Host = "db.example.com" |}
let corrected: Endpoint = { ...local; ...laterHost; Port = 5433 }
После переопределения типа укажите значение типа замены. Повторяющиеся явные поля, такие как { Host = "first"; Host = "second" }, остаются ошибками.
Для объявлений типов выигрышное поле отображается в позиции его выигрышного вклада. Поля из источника сохраняют исходный порядок. Этот порядок важен для конструкторов и соответствия подписей.
Оценка и копирование
Распределенные исходные выражения выполняются один раз в исходном порядке, даже если последующий вклад заменяет каждое поле, в который они предоставляются:
let visits = ResizeArray<string>()
let hostFrom source host =
visits.Add source
{| Host = host |}
let selected =
{| ...hostFrom "first" "localhost"
...hostFrom "second" "db.example.com" |}
assert (selected.Host = "db.example.com")
assert (Seq.toList visits = [ "first"; "second" ])
Распространение создает новую запись с неглубокими копиями полей и теми же преобразованиями, что и явные назначения. Изменяемые объекты, на которые ссылается поля, остаются общими.
Обновления вложенного поля работают с распространиями, так как они выполняются с копированием и обновлением:
let original = {| Connection = local; Retries = 3 |}
let changed = {| ...original; Connection.Port = 5433 |}
assert (changed.Connection.Port = 5433)
assert (original.Connection.Port = 5432)
Более поздний вклад Connection в замену предыдущего вложенного обновления в это поле. Поместите преднамеренные переопределения после распространения, которые они должны переопределить.
Предупреждения о теневых полях
Позже распространение может случайно отменить явный выбор. Компилятор сообщает об этих предупреждениях по умолчанию:
| Warning | Meaning |
|---|---|
| FS3897 | Поле типа распространяет тени более ранней явной объявления поля. |
| FS3898 | Поле из выражения распространяет тени более ранней явной привязки полей. |
Например, { Host = "db.example.com"; ...local } берется Host из local, а не "db.example.com".
Для аудита других перекрывающихся предупреждений включите следующие предупреждения:
| Warning | Meaning |
|---|---|
| FS3905 | Тип распространяет поле из более ранних типов. |
| FS3906 | Явное поле тенирует поле из предыдущего распространения. |
| FS3907 | Выражение распространяется на поле из более ранних выражений. |
<WarnOn>$(WarnOn);3905;3906;3907</WarnOn>
Эти уведомления не изменяют поведение самых правых побед.
Совместимость представлений и API
Тип распространяет мутабельность полей копирования и атрибуты уровня полей. Они не копируют атрибуты уровня типа; Собственный [<Struct>] атрибут назначения определяет его представление. Аргументы типа источника должны удовлетворять универсальным ограничениям источника, включая ограничения единиц измерения.
Important
Использование типа распространяется только в том случае, если общая эволюция является преднамеренной. Изменение исходного поля может изменить общедоступные поля назначения, конструктор, атрибуты или ограничения без изменения в этом назначении.
Распространение не пропускает проверки специальных возможностей. Допустимые значения NULL записи не являются допустимыми источниками распространения; установите, что значение, допускающее значение NULL, не равно null, прежде чем использовать его. Циклические зависимости через распространение отклоняются. Обычные рекурсивные поля записей не запрещаются только потому, что объявление также использует распространение.
Тип распространяется в .fsi файлах. Сигнатура может использовать явные поля при использовании реализации распространения или обратного, если полученные поля и их порядок согласуются. Кавычки содержат сложное построение записей, а не узел распространения.
Выбор распространений или копирования и обновления
Используйте выражения копирования и обновления для обновления одного типа из одного источника. Используйте распространение для объединения источников, проекта в другой тип записи или повторного использования объявлений полей. Не сочетайте with и ... в одном выражении записи.
Распространение не применяется к коллекциям, выражениям объектов, произвольным .NET объектам или записям C#. Они не предоставляют синтаксис шаблона записи. Конструкторы записей — это отдельная функция F# 11.2.