Postspridningsoperatorer

Om du vill dela fält utan att sammanfoga distinkta modelltyper använder du postspridningar som är tillgängliga från och med F# 11.

Använd ...Type för att bidra med fältdeklarationer till en ny namngiven post eller ...expression för att bidra med fältvärden till en namngiven eller anonym post. Återanvändning av deklarationer och värdemappning är oberoende alternativ.

Skapa posttyper

Om du vill dela anslutningsfält mellan konfigurationstyper sprids du Endpoint till en ny postdeklaration:

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

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

ClientOptions har Host, Portoch TimeoutSeconds. Det är en distinkt nominell typ, inte en Endpoint undertyp.

Flera källor och explicita deklarationer kan visas i samma typdefinition:

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

Endast postfält deltar. Metoder, egenskaper som inte är postfält och statiska medlemmar kopieras inte. En källa kan vara en namngiven post eller en anonym posttyp, inklusive en förkortning för en anonym post. Målet för en typspridning måste vara en namngiven posttyp.

Skapa postvärden

Kopiera slutpunkten till klientalternativ och använd sedan en anonym post för att ersätta värden:

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

Uttrycksspridningar accepterar namngivna eller anonyma postkällor och mål, inklusive structposter. Varje målfält måste få ett värde. En explicit målanteckning är användbar när flera namngivna posttyper delar etiketter.

Stärka ett fälts typ

En senare fältdeklaration kan ersätta typen av ett spridningsfält. Du kan till exempel representera en parsad identifierare med type CustomerId = CustomerId of System.Guidoch sedan skilja en importerad post från en med identifieraren:

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 validerar texten. spridningen kopierar bara fält. CustomerId särskiljer identifierare från andra GUID utan att införa ytterligare regler.

Kombinera oberoende modeller

Skriv deklarationer från en kuraterad nominell post och en anonym posttyp och sammanställ sedan värden från oberoende källor:

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

Namngivna och anonyma källor anger ProductCard. Kortet tillhandahåller sedan anonymt visningstillstånd. CanOrder beräknas en gång, inte beräknas om när en senare konstruktion ändrar antalet.

Project till ett känt mål

En måltyp väljer de fält som ska behållas. Flera källor kan tillhandahålla ersättningar:

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 håller bara Host och Port; production kombinerar källor och explicita åsidosättningar. Explicita fält som saknas från målet förblir fel.

En anonym post som inte har kommenterats tar alla bidragsfält. Ett namngivet mål måste kunna härledas. Okända källor behöver egna anteckningar, till exempel (source: ClientOptions) på en funktionsparameter.

Fältordning och åsidosättningar

Bidrag läse från vänster till höger. För ett upprepat fältnamn vinner det högra bidraget. Regeln gäller både fältdeklarationer i typer och fältvärden i uttryck.

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

Efter en typ åsidosättning anger du ett värde av ersättningstypen. Upprepade explicita fält, till exempel { Host = "first"; Host = "second" }, förblir fel.

För typdeklarationer visas ett vinnande fält vid positionen för det vinnande bidraget. Fält från en källa behåller sin källordning. Den här ordningen är viktig för konstruktorer och signaturefterlevnad.

Utvärdering och kopiering

Utspritt källuttryck körs en gång var, i källordning, även om ett senare bidrag ersätter varje fält som de tillhandahåller:

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

Spridningar skapar en ny post med ytliga fältkopior och samma konverteringar som explicita tilldelningar. Föränderliga objekt som refereras av fält förblir delade.

Kapslade fältuppdateringar fungerar med spridningar, som de gör med kopiering och uppdatering:

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

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

Ett senare bidrag till Connection ersätter en tidigare kapslad uppdatering av fältet. Placera avsiktliga åsidosättningar efter de spridningar som de ska åsidosätta.

Varningar om skuggade fält

En senare spridning kan oavsiktligt ångra ett explicit val. Kompilatorn rapporterar dessa varningar som standard:

Warning Meaning
FS3897 Ett fält från en typspridning skuggar en tidigare explicit fältdeklaration.
FS3898 Ett fält från ett uttryck som sprids skuggar en tidigare explicit fältbindning.

Tar till exempel { Host = "db.example.com"; ...local }Host från local, inte "db.example.com".

Om du vill granska andra överlappningar aktiverar du dessa opt-in-varningar:

Warning Meaning
FS3905 En typspridning skuggar ett fält från en tidigare typspridning.
FS3906 Ett explicit fält skuggar ett fält från ett tidigare uppslag.
FS3907 Ett uttryck som sprids skuggar ett fält från ett tidigare uttrycksspridning.
<WarnOn>$(WarnOn);3905;3906;3907</WarnOn>

Dessa meddelanden ändrar inte beteendet längst till höger vinner.

Representations- och API-kompatibilitet

Typ sprider kopieringsfältets föränderlighet och fältnivåattribut. De kopierar inte attribut på typnivå. målets eget [<Struct>] attribut avgör dess representation. Argument av källtyp måste uppfylla källans allmänna begränsningar, inklusive begränsningar för måttenheter.

Important

Använd endast typspridningar när delad utveckling är avsiktlig. En källfältsändring kan ändra målets offentliga fält, konstruktor, attribut eller begränsningar utan redigering till målet.

Spridningar kringgår inte hjälpmedelskontroller. Nullbara poster är inte giltiga spridningskällor. fastställa att ett null-värde inte är null innan du använder det. Cykliska beroenden via spridningar avvisas . Vanliga rekursiva postfält är inte förbjudna bara för att deklarationen också använder en spridning.

Typspridningar fungerar i .fsi filer. En signatur kan använda explicita fält medan implementeringen använder spridningar, eller det omvända, förutsatt att de resulterande fälten och deras ordning är överens. Citattecken innehåller den utarbetade postkonstruktionen, inte en spridningsnod.

Välj spridningar eller kopiera och uppdatera

Använd kopierings- och uppdateringsuttryck för en uppdatering av samma typ från en källa. Använd spridningar för att kombinera källor, projicera till en annan posttyp eller återanvända fältdeklarationer. Kombinera with inte och ... i samma postuttryck.

Spridningar gäller inte för samlingar, objektuttryck, godtyckliga .NET objekt eller C#-poster. De tillhandahåller inte postmönstersyntax. Postkonstruktorer är en separat F# 11.2-funktion.

Se även