Operatory spreadu rekordów

Aby udostępniać pola bez scalania odrębnych typów modeli, użyj rozrzutów rekordów, dostępnych począwszy od języka F# 11.

Służy ...Type do współtworzenia deklaracji pól do nowego nazwanego rekordu lub ...expression współtworzenia wartości pól do nazwanego lub anonimowego rekordu. Ponowne użycie deklaracji i mapowanie wartości to niezależne opcje.

Redagowanie typów rekordów

Aby udostępnić pola połączeń między typami konfiguracji, należy podzielić Endpoint na nową deklarację rekordu:

type Endpoint = { Host: string; Port: int }

type ClientOptions =
    { ...Endpoint
      TimeoutSeconds: int }

ClientOptions ma Hostwartości , Porti TimeoutSeconds. Jest to odrębny typ nominalny, a nie Endpoint podtyp.

Wiele źródeł i jawnych deklaracji może być wyświetlanych w tej samej definicji typu:

type Logging = { LogLevel: string }
type LoggedClientOptions = { ...ClientOptions; ...Logging }

Uczestniczą tylko pola rekordów. Metody, właściwości, które nie są polami rekordów, a statyczne elementy członkowskie nie są kopiowane. Źródło może być nazwanym rekordem lub typem rekordu anonimowego, w tym skrótem dla rekordu anonimowego. Miejsce docelowe rozprzestrzeniania typu musi być nazwanym typem rekordu.

Redaguj wartości rekordów

Skopiuj punkt końcowy do opcji klienta, a następnie użyj rekordu anonimowego, aby zastąpić hosta:

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

Rozkłady wyrażeń akceptują nazwane lub anonimowe źródła rekordów i miejsca docelowe, w tym rekordy struktury. Każde pole docelowe musi otrzymać wartość. Jawna adnotacja docelowa jest przydatna, gdy kilka nazwanych typów rekordów współużytkuje etykiety.

Wzmacnianie typu pola

Późniejsza deklaracja pola może zastąpić typ pola rozłożonego. Na przykład reprezentują identyfikator analizowany za pomocą type CustomerId = CustomerId of System.Guidelementu , a następnie odróżnić zaimportowany rekord od jednego z tym identyfikatorem:

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 weryfikuje tekst; pole rozkładu kopiuje tylko pola. CustomerId rozróżnia identyfikatory od innych identyfikatorów GUID bez nakładania dalszych reguł.

Łączenie niezależnych modeli

Utwórz deklaracje z wyselekcjonowanych rekordów nominalnych i anonimowego typu rekordu, a następnie złóż wartości z niezależnych źródeł:

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 |}

Źródła nazwane i anonimowe dostarczają ; ProductCardkarta dostarcza stan anonimowego widoku. CanOrder jest obliczany raz, a nie obliczany ponownie, gdy później konstrukcja zmienia liczbę.

Project do znanego celu

Typ docelowy wybiera pola, które mają być zachowywane. Wiele źródeł może dostarczać zamienniki:

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 program przechowuje tylko Host i Port; production łączy źródła i jawne przesłonięcia. Jawne pola nieobecne w obiekcie docelowym pozostają błędy.

Rekord anonimowy bez adnotacji przyjmuje wszystkie pola współautora. Nazwany element docelowy musi być wywnioskowalny. Nieznane źródła wymagają własnych adnotacji, takich jak (source: ClientOptions) parametr funkcji.

Kolejność pól i przesłonięcia

Współtworzenie jest odczytywane od lewej do prawej. W przypadku powtarzającej się nazwy pola najbardziej odpowiedni wkład wygrywa. Reguła ma zastosowanie zarówno do deklaracji pól w typach, jak i do wartości pól w wyrażeniach.

let laterHost = {| Host = "db.example.com" |}
let corrected: Endpoint = { ...local; ...laterHost; Port = 5433 }

Po zastąpieniu typu podaj wartość typu zastępczego. Powtarzające się jawne pola, takie jak { Host = "first"; Host = "second" }, pozostają błędami.

W przypadku deklaracji typów zwycięskie pole pojawia się na pozycji swojego zwycięskiego udziału. Pola ze źródła zachowują kolejność źródłową. Ta kolejność ma znaczenie dla konstruktorów i zgodności podpisów.

Ocena i kopiowanie

Wyrażenia źródłowe spreadu są uruchamiane raz, w kolejności źródłowej, nawet jeśli późniejsza współtworzenie zastępuje każde podane pole:

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" ])

Rozkłady tworzą nowy rekord z płytkimi kopiami pól i tymi samymi konwersjami co jawne przypisania. Obiekty modyfikowalne, do których odwołuje się pola, pozostają współużytkowane.

Zagnieżdżone aktualizacje pól działają z rozrzutami, podobnie jak w przypadku kopiowania i aktualizowania:

let original = {| Connection = local; Retries = 3 |}
let changed = {| ...original; Connection.Port = 5433 |}

assert (changed.Connection.Port = 5433)
assert (original.Connection.Port = 5432)

Późniejszy udział w Connection zamiany wcześniejszej zagnieżdżonej aktualizacji do tego pola. Umieść celowe przesłonięcia po rozprzestrzenianiach, które powinny zostać zastąpione.

Ostrzeżenia dotyczące pól w tle

Późniejsze rozłożenie może przypadkowo cofnąć jawny wybór. Kompilator domyślnie zgłasza te ostrzeżenia:

Warning Meaning
FS3897 Pole z typu rozłóż cienie wcześniejszej jawnej deklaracji pola.
FS3898 Pole z wyrażenia rozkłada cienie wcześniejszego jawnego powiązania pola.

Na przykład { Host = "db.example.com"; ...local } pobiera z Hostlocalelementu , a nie "db.example.com".

W przypadku inspekcji innych nakładających się na siebie ostrzeżeń włącz następujące ostrzeżenia dotyczące zgody:

Warning Meaning
FS3905 Typ rozkłada cień pola z wcześniejszego typu rozłożonego.
FS3906 Jawne pole cieniuje pole z wcześniejszego rozkładu.
FS3907 Wyrażenie rozprzestrzenia pole w tle z wcześniejszego rozkładu wyrażenia.
<WarnOn>$(WarnOn);3905;3906;3907</WarnOn>

Te uwagi nie zmieniają zachowania z prawej strony wygrywa.

Reprezentacja i zgodność interfejsu API

Typ rozdziela atrybuty pola kopiowania i atrybutów na poziomie pola. Nie kopiują atrybutów na poziomie typu; atrybut [<Struct>] miejsca docelowego określa jego reprezentację. Argumenty typu źródłowego muszą spełniać ogólne ograniczenia źródła, w tym ograniczenia jednostek miary.

Important

Używaj rozkładów typów tylko wtedy, gdy współdzielona ewolucja jest celowa. Zmiana pola źródłowego może zmieniać pola publiczne, konstruktora, atrybuty lub ograniczenia miejsca docelowego bez edytowania tego miejsca docelowego.

Spready nie pomijają kontroli ułatwień dostępu. Rekordy dopuszczane do wartości null nie są prawidłowymi źródłami rozrzutu; ustal, że wartość dopuszczana do wartości null jest niepusta przed jej użyciem. Zależności cykliczne przez spready są odrzucane. Zwykłe pola rekordów cyklicznych nie są zabronione tylko dlatego, że deklaracja używa również rozkładu.

Typ spreads działa w .fsi plikach. Podpis może używać jawnych pól, podczas gdy implementacja używa spreadów lub odwrotnie, pod warunkiem, że wynikowe pola i ich kolejność zgadzają się. Cudzysłów zawierają rozbudowaną konstrukcję rekordu, a nie węzeł rozłożony.

Wybieranie spreadów lub kopiowania i aktualizacji

Użyj wyrażeń kopiowania i aktualizacji dla aktualizacji tego samego typu z jednego źródła. Użyj rozrzutów, aby połączyć źródła, projekt do innego typu rekordu lub ponownie użyć deklaracji pól. Nie łącz with i ... w tym samym wyrażeniu rekordu.

Spready nie mają zastosowania do kolekcji, wyrażeń obiektów, dowolnych .NET obiektów ani rekordów języka C#. Nie zapewniają składni wzorca rekordu. Konstruktory rekordów są oddzielną funkcją języka F# 11.2.

Informacje dodatkowe