Neues in F# 4.5

F# 4.5 erweitert die Sprache F# mit mehreren Verbesserungen. Viele dieser Features wurden zusammen hinzugefügt, damit Sie effizienten Code in F# schreiben und gleichzeitig sicherstellen können, dass dieser Code sicher ist. Dies bedeutet, dass der Sprache einige Konzepte und eine entsprechende Compileranalysen hinzugefügt werden, wenn diese Konstrukte verwendet werden.

Erste Schritte

F# 4.5 ist in allen .NET Core-Distributionen und Visual Studio-Tools verfügbar. Weitere Informationen finden Sie unter Erste Schritte mit F#.

Span und byref-ähnliche Strukturen

Mit dem Typ Span<T>, der in .NET Core eingeführt wurde, können Sie Puffer im Arbeitsspeicher stark typisiert darstellen, was jetzt auch ab F# 4.5 in F# zulässig ist. Das folgende Beispiel zeigt, wie Sie eine Funktion, die Span<T> verwendet, mit unterschiedlichen Pufferdarstellungen wiederverwenden können:

let safeSum (bytes: Span<byte>) =
    let mutable sum = 0
    for i in 0 .. bytes.Length - 1 do
        sum <- sum + int bytes[i]
    sum

// managed memory
let arrayMemory = Array.zeroCreate<byte>(100)
let arraySpan = new Span<byte>(arrayMemory)

safeSum(arraySpan) |> printfn "res = %d"

// native memory
let nativeMemory = Marshal.AllocHGlobal(100);
let nativeSpan = new Span<byte>(nativeMemory.ToPointer(), 100)

safeSum(nativeSpan) |> printfn "res = %d"
Marshal.FreeHGlobal(nativeMemory)

// stack memory
let mem = NativePtr.stackalloc<byte>(100)
let mem2 = mem |> NativePtr.toVoidPtr
let stackSpan = Span<byte>(mem2, 100)

safeSum(stackSpan) |> printfn "res = %d"

Ein wichtiger Aspekt ist, dass Span und andere byref-ähnliche Strukturen sehr starre statische Analysen aufweisen, die vom Compiler durchgeführt werden, die ihre Verwendung möglicherweise unerwartet einschränken. Dies ist der grundlegende Kompromiss zwischen Leistung, Ausdrucksfähigkeit und Sicherheit in F# 4.5.

Überarbeitete Byrefs

Vor F# 4.5 waren Byrefs in F# für zahlreiche Anwendungen unsicher und unzuverlässig. Zuverlässigkeitsprobleme in Bezug auf Byrefs wurden in F# 4.5 behoben. Darüber hinaus wird ebenfalls die gleiche statische Analyse, die für span und byref-ähnliche Strukturen durchgeführt wird, angewendet.

inref<'T> und outref<'T>

Um das Konzept eines Nur-Lese-, Nur-Schreib- und verwalteten Lese/Schreib-Zeigers darzustellen, führt F# 4.5 die Typen inref<'T>, outref<'T>, die Nur-Lese- und Nur-Schreib-Zeiger darstellen. Beide weisen eine andere Semantik auf. Sie können z. B. nicht in ein inref<'T> schreiben:

let f (dt: inref<DateTime>) =
    dt <- DateTime.Now // ERROR - cannot write to an inref!

Standardmäßig leitet der Typrückschluss verwaltete Zeiger entsprechend der unveränderlichen Natur von F#-Code als inref<'T> ab, es sei denn, etwas wurde bereits als änderbar deklariert. Um etwas schreibbar zu machen, müssen Sie einen Typ wie mutable deklarieren, bevor Sie seine Adresse an eine Funktion oder einen Member übergeben, der ihn bearbeitet. Weitere Informationen finden Sie unter Byrefs.

Schreibgeschützte Strukturen

Ab F# 4.5 können Sie eine Struktur mit IsReadOnlyAttribute als Anmerkung versehen, z. B.:

[<IsReadOnly; Struct>]
type S(count1: int, count2: int) =
    member x.Count1 = count1
    member x.Count2 = count2

Dies verhindert das Deklarieren eines veränderlichen Members in der Struktur und gibt Metadaten aus, die F# und C# eine Behandlung als schreibgeschütztes Element bei Verwendung von einer Assembly ermöglichen. Weitere Informationen finden Sie unter ReadOnly-Strukturen.

Void-Zeiger

Der voidptr-Typ wurde ebenso wie die folgenden Funktionen zu F# 4.5 hinzugefügt:

  • NativePtr.ofVoidPtr, um einen void-Zeiger in einen nativen int-Zeiger zu konvertieren
  • NativePtr.toVoidPtr, um einen nativen int-Zeiger in einen void-Zeiger zu konvertieren

Dies ist für die Interoperabilität mit einer nativen Komponente hilfreich, die void-Zeiger verwendet.

dem match!-Schlüsselwort

Das match!-Schlüsselwort erweitert den Musterabgleich in einem Berechnungsausdruck:

// Code that returns an asynchronous option
let checkBananaAsync (s: string) =
    async {
        if s = "banana" then
            return Some s
        else
            return None
    }

// Now you can use 'match!'
let funcWithString (s: string) =
    async {
        match! checkBananaAsync s with
        | Some bananaString -> printfn "It's banana!"
        | None -> printfn "%s" s
}

Dadurch können Sie Code kürzen, in dem häufig Optionen (oder andere Typen) mit Berechnungsausdrücken wie async kombiniert werden. Weitere Informationen finden Sie unter match!.

Gelockerte Upcastinganforderungen in Array-, Listen- und Sequenzausdrücken

Beim Mischen von Typen, bei denen einer innerhalb von Array-, Listen- und Sequenzausdrücken von einem anderen erben kann, mussten Sie traditionell jeden abgeleiteten Typ mit :> oder upcast in den übergeordneten Typ umwandeln. Dies wurde nun wie folgt gelockert:

let x0 : obj list  = [ "a" ] // ok pre-F# 4.5
let x1 : obj list  = [ "a"; "b" ] // ok pre-F# 4.5
let x2 : obj list  = [ yield "a" :> obj ] // ok pre-F# 4.5

let x3 : obj list  = [ yield "a" ] // Now ok for F# 4.5, and can replace x2

Lockerung beim Einzug für Array- und Listenausdrücke

Vor F# 4.5 mussten Sie Array- und Listenausdrücke bei der Übergabe als Argumente an Methodenaufrufe übermäßig einziehen. Dies ist nicht mehr erforderlich:

module NoExcessiveIndenting =
    System.Console.WriteLine(format="{0}", arg = [|
        "hello"
    |])
    System.Console.WriteLine([|
        "hello"
    |])