Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Vítá vás F# 11! Tato verze vám pomůže zachovat jedinečné doménové modely, opakovaně používat obecné algoritmy, koordinovat asynchronní práci a snižovat využití paměti kompilátoru.
- Rozbalování záznamů umožňuje znovu použít pole, aniž by model úložiště a aplikační model musely sdílet stejný typ.
- Členy rozšíření v generických algoritmech umožňují existujícím typům sdílet operace bez obalových tříd.
- integrace .NET zachovává metadata delegáta a znovu používá dokumentaci k rozhraní API.
- Asynchronní pomocné rutiny a pomocné rutiny úloh přenášejí zrušení přes hranice rozhraní API a omezují souběžnou práci.
- Vylepšení kompilátoru a knihovny snižují opakovanou analýzu a vyhněte se určitým přidělením.
Děkujeme komunitě F# za návrhy, implementace, kontroly a testy, které stojí za těmito změnami. Odkazy na přispěvatele doprovázejí níže uvedené funkce.
Note
Příklady vyžadují kompilátor F# 11 a FSharp.Core 11 s podporou uvedených funkcí. V sadě SDK verze .NET 11.0.100 vyžadují <LangVersion>preview</LangVersion>funkce F# 11.2 ; viz nastavení jazyka a plánovaná podpora sady SDK. Příklad runtime-async má další požadavky na modul runtime; samotná starší sada .NET 11 SDK nemusí obsahovat všechny změny.
Rozložení záznamů: jedinečné modely bez opakovaných mapování
Aby modely pro úložiště a aplikaci zůstaly oddělené, rozprostření záznamů znovu využívá jejich sdílená pole bez opakovaných deklarací. Zde modely sdílejí údaje o zákaznících, ale používají různé typy identifikátorů:
open System
type CustomerId = CustomerId of Guid
type CustomerDetails = { Name: string; PreferredLanguage: string }
type CustomerRow =
{ ...CustomerDetails
Id: string
InternalNote: string }
type Customer =
{ ...CustomerDetails
Id: CustomerId }
...CustomerDetails přispívá deklaracemi polí. Na hranici se hodnoty zkopírují pomocí ...row, zatímco převod analyzuje identifikátor:
let toCustomer (row: CustomerRow) =
match Guid.TryParse row.Id with
| true, id -> Ok ({ ...row; Id = CustomerId id }: Customer)
| false, _ -> Error "Invalid customer identifier."
Cíl zachová sdílené údaje, nahradí Id a vyloučí InternalNote.
Guid.TryParse, nikoli spread operátor, validuje identifikátor; neplatný vstup způsobí Error.
Řeší se tím problém mapování na aplikační vrstvě. Rozpětí hodnot také kombinují pojmenované a anonymní záznamy; Rozložení typu vytváří pouze pojmenované záznamy. Pozdější pole mají přednost a zdroje se spustí jednou v pořadí. Příklady složení a rizika sdíleného vývoje najdete v tématu Operátory rozložení záznamů .
Díky @brianrourkeboll za rekordní rozpětí.
Členy rozšíření pro obecné algoritmy, operátory a uspořádané n-tice
Pokud chcete sdílet algoritmus mezi existujícími typy, F# 11.2 umožňuje, aby členy rozšíření splňovaly omezení staticky řešeného parametru typu (SRTP). Jedna transformace může například zpracovávat seznamy a volby bez obalových typů.
Note
V .NET 11.0.100 SDK povolte <LangVersion>preview</LangVersion>, kde se rozšíření vyřeší, a otevřete modul rozšíření. Viz požadavky na knihovnu.
Sdílet transformaci mezi seznamy a možnostmi
S map knihovny v dosahu může aplikace vyčistit kteroukoli z obou reprezentací, změnit typy elementů a vytvářet transformace:
let inline cleanNames values =
map (fun (name: string) -> name.Trim().ToUpperInvariant()) values
let batch: string list = cleanNames [ " Ada "; " grace " ]
let optional: string option = cleanNames (Some " linus ")
let missing: string option = cleanNames (None: string option)
let lengths: int list = map String.length batch
let nested: int option list = map (map String.length) [ Some "abc"; None ]
printfn "%A; %A; %A" batch optional missing
printfn "%A; %A" lengths nested
// ["ADA"; "GRACE"]; Some "LINUS"; None
// [3; 5]; [Some 3; None]
Adaptéry v Structural.Adapters používají známé operace, místo vnucování nových kontejnerů:
type List<'T> with
static member Map(source: 'T list, mapping: 'T -> 'U) : 'U list =
List.map mapping source
type Option<'T> with
static member Map(source: 'T option, mapping: 'T -> 'U) : 'U option =
Option.map mapping source
Sdílená funkce v Structural.Algorithms požádá vstupní typ o statický člen Map:
let inline map (mapping: 'T -> 'U) (source: ^Input) : ^Output =
(^Input :
(static member Map: ^Input * ('T -> 'U) -> ^Output)
(source, mapping))
^Input a ^Output jsou staticky vyřešené typy. Při každém konkrétním volání kompilátor vybere člena s požadovaným podpisem. Rozšíření nepřidávají implementaci rozhraní. Kompletní aplikace otevře oba moduly.
Operátory, kontext a výstupní typy
Stejný mechanismus podporuje operátory n-tic a šíření kontextu a konstrukci řízenou výstupem. Nativní záhlaví n-tic vyžadují povolení funkce F# 11.2 ve svých deklaracích a také v místech volání při řešení rozšíření.
Samostatný doplněk, AllowOverloadOnReturnTypeumožňuje jedné továrně deklarovat přetížení rozlišující jejich návraty:
type Inject =
[<AllowOverloadOnReturnType>]
static member Pure(value: 'T) : 'T list = [value]
[<AllowOverloadOnReturnType>]
static member Pure(value: 'T) : 'T option = Some value
let batch: string list = Inject.Pure "ready"
let optional: string option = Inject.Pure "ready"
// ["ready"] and Some "ready"
Tento atribut Core 11 nevyžaduje náhled. Anotace výsledku vybere přetížení; bez ní zůstane volání nejednoznačné. Viz Výběr podle návratového typu.
Díky @gusty za rozšíření podpory obecného programování.
integrace .NET a zkušenosti při vývoji
Přímí delegáti zachovávají skutečné rozhraní obslužné rutiny.
Aby bylo možné frameworkům zpřístupnit názvy obslužných funkcí a atributy vazeb, může F# 11 místo obálky cílit přímo na kompatibilní pojmenovanou funkci. ASP.NET Core minimálních rozhraní API pak můžou číst trasu, službu a kontrakt hlaviček:
open System
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Mvc
open Microsoft.Extensions.DependencyInjection
type ShipmentStore() =
member _.Status trackingId =
if trackingId = "SHP-1042" then "Dispatched" else "Unknown"
let status
(trackingId: string)
([<FromServices>] store: ShipmentStore)
([<FromHeader(Name = "X-Customer")>] customer: string) =
$"{customer}: {trackingId} is {store.Status trackingId}"
let handler = Func<string, ShipmentStore, string, string>(status)
Vizte spustitelný projekt a pravidla pro konstrukci/kompatibilitu. Zachytávání a přizpůsobení argumentů může stále vyžadovat obálky.
Děkujeme @kerams za přímé delegace.
Opětovné použití dokumentace XML
Chcete-li sdílené komentáře k API udržovat na jednom místě, umístěte je do shared.xml:
<docs>
<tracking>
<remarks>An unrecognized tracking identifier has status Unknown.</remarks>
</tracking>
</docs>
Když je povoleno GenerateDocumentationFile, F# 11 rozvine <include> do vygenerovaných položek členů:
/// <summary>Find the latest status of a shipment.</summary>
/// <include file="shared.xml" path="/docs/tracking/*"/>
let status trackingId =
if trackingId = "SHP-1042" then "Dispatched" else "Unknown"
/// <summary>Find the latest statuses of several shipments.</summary>
/// <include file="shared.xml" path="/docs/tracking/*"/>
let statuses trackingIds =
trackingIds |> List.map (fun id -> id, status id)
Implementace include aktualizuje oba popisy z jednoho fragmentu.
Resolver nástrojů samostatně zpřístupňuje základní popisy pro <inheritdoc/>; kompilátor tuto značku ponechává pro konzumenty dokumentace. Viz dokumentaci XML, kde najdete příklady a pravidla určování.
Děkujeme @T-Gro za sdílenou dokumentaci XML v FSharp.Core.
Ladění inline funkcí v neoptimalizovaných sestaveních
Chcete-li prozkoumat argumenty vložené pomocné funkce, nastavte zarážku na let total v neoptimalizovaném sestavení a přejděte krokováním do addFee. F# 11 teď dává přednost volatelným metodám v těchto buildech:
let inline addFee (fee: decimal) (amount: decimal) =
let total = amount + fee
total
let invoiceTotal = addFee 2m 40m
printfn "%M" invoiceTotal
// 42
Optimalizovaná sestavení zachovávají inline expanzi. Některé konstrukty se stále musí rozvinout. Viz část Ladění vložených funkcí, kde najdete informace o těchto omezeních a ovládacím prvku --always-inline.
Asynchronnost a koordinace úkolů
Pro připojení pracovních postupů jazyka F# pomocí rozhraní API založených na úlohách přidává FSharp.Core 11 propojení podporující zrušení, omezené dávky a transformace výsledků.
Omezit počet nevyřízených vstupně-výstupních operací, nejen kolekci výsledků
Velká dávka čtení může zahájit více operací, než by aplikace měla mít současně rozpracovaných. K rozhodnutí o tom, kdy práce založená na úlohách začne, potřebuje koordinátor factory, nikoli již spuštěné úlohy:
open System.IO
open System.Threading
let readFiles (paths: seq<string>) (ct: CancellationToken) =
paths
|> Seq.map (fun path ->
fun token -> File.ReadAllTextAsync(path, token))
|> Task.parallelLimit 4 ct
Nanejvýš čtyři operace čtení probíhají současně; výsledky zachovávají pořadí vstupu. Selhání vyžaduje zrušení souvisejících požadavků prostřednictvím zadaných tokenů. To omezuje souběžnost, nikoli počet požadavků za sekundu. Viz funkce Async a task pro sekvenční varianty a varianty s výsledkem typu unit.
Zachovejte zrušení a původní výjimku přes hranici
Když pracovní postup spustí externí práci, měl by jeho token cestovat s touto externí prací.
Async.StartTaskImmediate předá token spuštěného pracovního postupu do objektu pro vytváření úloh a čeká na dokončení:
open System.IO
let tryReadFile path =
async {
try
let! contents =
Async.StartTaskImmediate(fun ct -> File.ReadAllTextAsync(path, ct))
return Some contents
with :? FileNotFoundException ->
return None
}
Selhání jediného úkolu je zpracováno touto obslužnou rutinou jako základní FileNotFoundException. Pro již vytvořený úkol nebo ValueTask použijte Async.Await; starší Async.AwaitTask zachovává chování spočívající v zapouzdření výjimek.
Pro volající používající úlohy Task.startAsyncImmediate token workflow poskytuje reverse bridge. Spustí se na volajícím vlákně do prvního asynchronního pozastavení.
Používejte odpovídající pomocné funkce pro krátké transformace
Chcete-li transformovat hodnotu typu Result bez úplného výpočetního výrazu, použijte odpovídající pomocné rutiny v Microsoft.FSharp.Control. Například načtěte soubor, zjistěte jeho délku a vraťte běžné selhání jako Error:
open System.IO
open System.Threading
let fileLength (path: string) (ct: CancellationToken) =
File.ReadAllTextAsync(path, ct)
|> Task.map String.length
|> Task.catch
Soubor obsahující Report 1 vrátí Ok 8; chybějící soubor poskytuje Error obsahující FileNotFoundException. Zrušení se stále projeví.
Async a ValueTask nabízejí odpovídající mapfunkce, binda pomocné funkce pro chyby; viz asynchronní funkce a funkce úloh. Pro větve nebo několik závislých hodnot použijte výpočetní výraz.
Pomocné rutiny ValueTask vyžadují sestavení netstandard2.1 Core nebo net10.0 a jednorázové použití.
Děkujeme @bartelink za příspěvky k rozhraní API pro async/task.
Opětovné vyvolání po asynchronním hlášení selhání
Provozní protokolování by mělo přidávat informace, aniž by měnilo to, co volající zachytí, nebo skrývalo, kde selhání začalo. Obslužné rutiny výpočetních výrazů teď mohou použít reraise () i poté, co čekají na protokolovací modul:
Note
Rethrow ve výpočetním výrazu je funkcionalita F# 11.2. V .NET 11.0.100 SDK povolte <LangVersion>preview</LangVersion>.
open System
open System.IO
open System.Threading.Tasks
let readReport (logFailure: exn -> Task) path =
task {
try
return! File.ReadAllTextAsync(path)
with :? IOException as error ->
do! logFailure error
return reraise ()
}
Po úspěšném zaznamenání do protokolu se dál šíří stejná výjimka i původní informace o místě jejího vyvolání. Při selhání protokolovacího nástroje může být původní záznam nahrazen. To řeší požadavek na asynchronní pozorovatelnost bez runtime asynchronnosti. Viz Opětovné vyvolání výjimky z výpočetního výrazu.
Děkujeme @xperiandri za tuto podporu.
FSharp.Core může vycházet z moderního .NET
Pokud chcete používat moderní rozhraní API bez vyřazení starších cílů, přidává net10.0 funkce FSharp.Core vedle netstandard2.0 sebe a netstandard2.1. NuGet vybere kompatibilní sestavení. Aktualizované balení nástrojů také umožňuje asynchronní uvolňování prostředků založené na úlohách v F# Interactive, stejně jako v kompilovaných projektech. Viz cílové architektury FSharp.Core.
@T-Gro Děkujeme za cílení na moderní Core a balení nástrojů SDK.
Runtime async pro autory sestavení
Autoři tvůrce můžou nechat modul runtime pozastavit a obnovit označené metody místo použití stavových počítačů generovaných kompilátorem. Experimentální builder definovaný knihovnou zpřístupňuje mechanismus jako výpočetní výraz:
let readManifest (path: string) =
runtimeTask {
let! text = System.IO.File.ReadAllTextAsync path
return text.Length
}
Note
Tento experimentální tvůrce používá funkci F# 11.2, kde je definována nebo rozšířena její vložená implementace; v sadě .NET 11.0.100 SDK povolte verzi Preview. Vyžaduje také sestavení a reference Core 11 net10.0 a modul runtime s podporou runtime async. Tady je ověřená kombinace runtime/reference .NET 11 RC1. Cílení na .NET 10 není dostatečné. Viz asynchronní experimenty runtime.
FSharp.Core tuto ukázku nedoručí jako tvůrce pro obecné účely. Běžný kód task a async se na něj automaticky nepřepíná a volající již zkompilovaného rozhraní API vracejícího úlohu preview ze své podstaty nepotřebují.
Díky @majocha za asynchronní runtime.
Nativní text vhodný pro AOT a dvojrozměrná pole
Aby bylo možné podporovat nativní AOT, F# 11 odebere z běžnějších operací s textem a poli požadavky na formátování založené na reflexi a dynamické přidělování.
Tisk hodnot a sestavování řetězců bez redundantní vrstvy formátu
Pokud už máte řetězec, k jeho zápisu nepotřebujete jen značku "%s". Nové funkce Core print a printn přijímají hodnoty pomocí převodu string jazyka F#; printn přidá znak nového řádku:
let report (processed: int) (total: decimal) (elapsed: TimeSpan) : string =
$"{processed} items, total {total:F2}, elapsed {elapsed.TotalMilliseconds:F0} ms"
printn (report 12 145.50m (TimeSpan.FromMilliseconds 250.0))
// 12 items, total 145.50, elapsed 250 ms
Interpolace řetězců používá přímý převod a String.Concat ve více případech. Zástupné symboly formátu .NET, jako je {total:F2}, používají invariantní formátování a provádějí boxing hodnoty; %A a formáty printf s určenou přesností mohou i nadále potřebovat formátování printf. Vyberte explicitní národní prostředí pro lokalizovaný výstup.
S --reflectionfree záznamy a sjednocené typy také získají vygenerovaný ToString výstup. Příklady kultur a podporované cesty najdete v tématu Interpolace a nativní AOT.
Děkujeme @bbatsov za funkce pro tisk a @charlesroddie za implementaci interpolace.
Zachovat běžné operace mřížky s indexováním od nuly
Mřížka založená na nule by neměla vyžadovat náhradní modul Array2D jen proto, aby se zabránilo cestě alokace dynamického kódu. Aktualizované základní implementace podporují známou inicializaci a transformaci:
let grid = Array2D.init 3 4 (fun row column -> row * 4 + column)
let doubled = grid |> Array2D.map (fun value -> value * 2)
let snapshot = Array2D.copy doubled
printn $"{snapshot.GetLength(0)} x {snapshot.GetLength(1)}; last = {snapshot[2, 3]}"
// 3 x 4; last = 22
Array2D.create, rebase a init používají cestu přidělení založenou na nule napříč sestaveními Core. Implementace zachovávající hranice map, mapi a copy mohou v sestavení net10.0 používat novější rozhraní API pro přidělování .NET; sestavení .NET Standard si zachovávají záložní řešení. Pole s indexováním od nenuly nejsou zastaralá, ale jejich nativní omezení přidělení zůstávají.
Informace o cestách přidělení a zbývajících omezeních najdete v článku Dvourozměrná pole a Native AOT. Zkontrolujte diagnostiku publikování celé aplikace.
Díky @T-Gro za vylepšení Array2D.
Menší využití paměti a méně opakované práce
F# 11 snižuje opakovanou analýzu kompilátoru a některé alokace v aplikaci. Tyto úlohy mají vliv na různé úlohy.
Sdílení odkazovaných knihoven napříč projekty
Aby nedocházelo k opakovaným kopiím sdílených binárních závislostí, může služba jazyka sdílet vhodné importované grafy knihoven napříč projekty.
Při retained-memory experimentu implementačního PR kleslo řešení ReSharper.FSharp o deseti projektech z 1 547,2 MB na 1 002,2 MB s povoleným sdílením. To je měření typu zapnuto/vypnuto pro danou úlohu, nikoli obecné snížení F# z 10 na 11. Řešení, v nichž převažují odkazy na projekty spíše než sdílené binární odkazy, můžou zaznamenat mnohem menší zlepšení.
Čtení metadat a vložená specializace také snižují dočasné přidělení, samostatnou metriku. Viz Výkon nástrojů pro rozsah a ovládání.
Díky @auduchinok pro importy sdílených knihoven.
Kompilovat nezávislé úlohy paralelně ve výchozím nastavení
Paralelní kompilace je nyní ve výchozím nastavení povolena, přičemž je zachován deterministický výstup i pravidla pořadí zdrojových souborů. Přetížení ukládání do mezipaměti a vylepšení strážených vzorů zabraňuje dalším opakovaným pracím. Visual Studio/FCS také snižují analýzu nezměněných dokumentů a omezují souběžné kontroly; viz Výkon nástrojů.
Vyhněte se callbacku a režii přidělení prázdných výsledků
Vybrané List funkce nyní mají Array vložená zpětná volání. Pro své vlastní pomocné funkce OptimizeClosureIfNotInlined vkládané přímo do kódu jednorázově adaptuje neprůhledný curryovaný callback:
let inline foldItems
([<InlineIfLambda; OptimizeClosureIfNotInlined>] folder)
state
(items: 'T[]) =
let mutable total = state
for item in items do
total <- folder total item
total
Ostatní přidělení můžou zůstat. Požadavky najdete v Optimalizace částečně aplikovaných zpětných volání.
Prázdné výsledky jsou dalším běžným případem úspěšně odvedené práce. Motivační pracovní zátěž FSharpLintu opakovaně kombinovala pravidla, která nevygenerovala žádná varování. Jádro teď znovu používá sdílená prázdná pole v dalších operacích:
let checkTitle (title: string) =
if String.IsNullOrWhiteSpace title then [|"A title is required."|]
else Array.empty
let warnings = ["Introduction"; "Installation"] |> List.toArray |> Array.collect checkTitle
let sharedEmpty = obj.ReferenceEquals(warnings, Array.empty<string>)
// true: no new empty result array
Tato změna odebere redundantní objekty prázdného pole, ne každou alokaci v kanálu. Nespoléhejte na to, že samostatné prázdné výsledky mají odlišné referenční identity.
Díky zpětnému @T-Grovolání vložené kolekce a sdíleným prázdným polím.
Přesnější volání rozhraní API a každodenní usnadnění
Vyžadovat názvy, když se může změnit pořadí generovaných parametrů
Aby autor chránil vygenerovaná rozhraní API před tichými změnami pořadí parametrů, může vyžadovat pojmenované argumenty:
Note
Vynucení je funkce F# 11.2; v sadě .NET 11.0.100 SDK povolte verzi Preview. Vyžaduje se také dostupná System.Diagnostics.CodeAnalysis.RequireNamedArgumentsAttribute definice.
Kompletní ukázka poskytuje definici kompatibility pro reference, kterým chybí definice kompatibility.
open System.Diagnostics.CodeAnalysis
type Query =
[<RequireNamedArguments>]
static member Execute(numerator: decimal, denominator: decimal) =
numerator / denominator
let ratio = Query.Execute(numerator = 3m, denominator = 4m)
// 0.75M; Query.Execute(3m, 4m) is rejected.
Přeuspořádání deklarace zachovává výsledek pojmenovaného volání, 0.75M; poziční volání je odmítnuto. Pro vygenerovaný případ použití rozhraní API musí poskytovatel emitovat anotaci.
Rozpoznat již zabalený obecný argument
Chcete-li zabránit zabalení existujícího úkolu jako hodnoty prostého výsledku, nové záchranné pravidlo přetížení dává přednost Task<'T> před holým 'T:
Note
Nejkonkrétnější pravidlo pro rozhodnutí shody a chování priority přetížení jsou funkce F# 11.2; v sadě SDK .NET 11.0.100 povolte verzi Preview.
open System.Threading.Tasks
let pending = Task.FromResult 42
let wrapped = ValueTask<_>(pending)
let answer: int = wrapped.Result
// ValueTask<int>, not ValueTask<Task<int>>
Výsledek je ValueTask<int>, ne ValueTask<Task<int>>. Limity pravidla najdete v části Výběr přetížení.
Preferujte nové přetížení bez odebrání starého entry pointu.
Knihovna může zachovat starou diagnostickou metodu pro existující binární soubory při přidávání přetížení, které zahrnuje zdrojové umístění volajícího.
OverloadResolutionPriorityAttribute dává přednost nově zkompilovaným volajícím:
open System.Runtime.CompilerServices
type Check =
static member That(condition: bool) =
if condition then "OK" else "Check failed."
[<OverloadResolutionPriority(1)>]
static member That(condition: bool, [<CallerLineNumber>] ?line: int) =
if condition then "OK" else $"Check failed at line {defaultArg line 0}."
let stock = 0
let diagnostic = Check.That(stock > 0)
// The diagnostic includes the caller's source line.
Nezměněné volání vybere přetížení zohledňující řádky. Viz Výběr přetížení metod pro pravidla rozsahu a kompatibility.
Použití podmíněných záruk nulovatelnosti v platformě .NET
Pokud je vaše cesta k souboru nenulová, neměl by výsledek rozhraní Path API s možnou hodnotou null vynutit kontrolu, kterou jeho vlastní kontrakt vylučuje. F# 11 využívá existující .NET NotNullIfNotNull příslib: nenulový pojmenovaný vstup znamená nenulový výsledek.
open System.IO
let fileName: string = Path.GetFileName("reports/annual.csv")
let extension: string = Path.GetExtension(fileName)
printn (extension.ToUpperInvariant())
// .CSV
let fileNameOf (path: string | null) : string | null =
Path.GetFileName path
Povolte <Nullable>enable</Nullable>. Vstupy s možnou hodnotou null stále potřebují ošetření hodnot null; viz podmíněné kontrakty návratové hodnoty. Pro neomezená rozhraní API jazyka C# T? použijte samostatnou Unchecked.withNull pomocnou rutinu.
Díky @kerams kvůli podpoře podmíněné nulové hodnoty.
Menší vylepšení jazyka
Chcete-li volat typ záznamu pomocí syntaxe konstruktoru .NET, použijte v jazyce F# 11.2 konstruktory záznamů:
type Person = { Name: string; Age: int }
let person = Person(Name = "Ada", Age = 36)
Pojmenované argumenty se vyhýbají závislosti na pořadí parametrů. Díky @charlesroddie za tuto funkci.
U podmíněného zdroje #elif se vyhnete vnořeným skupinám #if :
#if EU_WAREHOUSE
let dispatchTimeZone = "Europe/Paris"
#elif US_WAREHOUSE
let dispatchTimeZone = "America/New_York"
#else
let dispatchTimeZone = "UTC"
#endif
System.Console.WriteLine(dispatchTimeZone)
První odpovídající větev vyhraje. Ukázkový projekt definuje EU_WAREHOUSE a vytiskne Europe/Paris; pokud není definován ani jeden symbol, výstup je UTC.
Související nástroje a scénáře interoperability:
- K identifikaci interaktivní sady nástrojů použijte
#version. - Pokud chcete sdílet zdroj s externím hostitelem, F# přijímá
#:řádky direktiv hostitele bez toho, aby je interpretoval jako pokyny pro balíček nebo sestavení. - Pokud se chcete vyhnout redundantním deklaracím rozhraní, použijte výchozí implementaci odvozeného rozhraní.
- Pokud chcete číst zděděný stav uvnitř běžné closure, použijte novou podporu chráněných polí základní třídy.
Před upgradem
Při aktualizaci mějte na paměti tyto významné změny:
| Ovlivněný kód | Action |
|---|---|
Knihovny se .fsi soubory |
Kopírovat vynucené atributy kontraktů do podpisů; vynechání jsou nyní chybami FS3888. |
| Vložené knihovny založené na rozšířeních | Zkontrolujte odvozené podpisy a požadavky producenta/příjemce. Odvození F# 11.2 může zachovat obecnější omezení. |
| Delegáti metod instance | Ověřte příjemce před přímou konstrukcí, protože při příjemci s hodnotou null nyní dochází k selhání. |
| Lokalizovaný interpolovaný výstup | Formátovací položky ve formátu .NET používají invariantní jazykovou verzi. Zvolte explicitní jazykovou a místní verzi, pokud je určen lokalizovaný výstup. |
Vlastní tvůrci s finální do! |
Dodání vazby a dokončení jednotek; final do! už nevybírej konečné členy přeposílání. |
Nedokončené let! |
Přidejte následující tělo výpočtu; neúplná vazba teď má přímočařejší diagnostické hlášení. |
| Předchozí jazyková nastavení | Podporované číslované režimy začínají na F# 8; režim nestriktního odsazení je odstraněn. Viz Jazyková verze a odsazení. |
| Bitové operace s enum | Používejte výčty s celočíselným podkladovým typem; neplatné podkladové typy jsou odmítnuty. |