F# 4.5 的新功能
F# 4.5 新增了 F# 語言的多項改善。 其中許多功能一起新增,可讓您在 F# 中撰寫有效率的程式碼,同時確保此程式碼安全無虞。 這樣做表示在使用這些建構時,將一些概念新增至語言和大量的編譯器分析。
開始使用
F# 4.5 適用於所有 .NET Core 散發套件和 Visual Studio 工具。 開始使用 F# 以深入了解。
範圍和類似 byref 的結構
.NET Core 中引進的 Span<T> 型別可讓您以強型別的方式來表示記憶體中的緩衝區,現在從 F# 4.5 開始允許在 F# 中使用。 下列範例示範如何重複使用以不同緩衝區表示法在 Span<T> 上運作的函式:
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"
這一點的一個重要層面是範圍和其他類似 byref 的結構具有非常嚴格的靜態分析,由編譯器執行,以您可能會發現非預期的方式限制其使用方式。 這是 F# 4.5 中引進的效能、表達性和安全性之間的基本取捨。
改寫 byrefs
在 F# 4.5 之前,F# 中的 Byrefs 對許多應用程式而言是不安全且不健全的。 F# 4.5 中已解決 byref 的健全性問題,而且也會套用針對範圍和類似 byref 的結構進行的相同靜態分析。
inref<'T> 和 outref<'T>
為了表示唯讀、唯寫和讀寫受控指標的概念,F# 4.5 引進了 inref<'T>
、outref<'T>
型別,分別代表唯讀指標和唯寫指標。 每個都有不同的語意。 例如,您無法寫入 inref<'T>
:
let f (dt: inref<DateTime>) =
dt <- DateTime.Now // ERROR - cannot write to an inref!
根據預設,型別推斷會將受控指標推斷為 inref<'T>
,與 F# 程式碼不可變的本質一致,除非某些項目已經宣告為可變動。 若要讓某個項目成為可寫入,您必須先將型別宣告為 mutable
,再將其位址傳遞至操作該型別的函式或成員。 若要深入了解,請參閱 Byrefs。
唯讀結構
從 F# 4.5 開始,您可以使用 IsReadOnlyAttribute 標註結構,例如:
[<IsReadOnly; Struct>]
type S(count1: int, count2: int) =
member x.Count1 = count1
member x.Count2 = count2
這可讓您在結構中宣告可變成員,並發出中繼資料,讓 F# 和 C# 在從元件取用時將其視為唯讀。 若要深入了解,請參閱 ReadOnly 結構。
void 指標
voidptr
型別會新增至 F# 4.5,如下列函式所示:
NativePtr.ofVoidPtr
以將 void 指標轉換為原生 int 指標NativePtr.toVoidPtr
以將原生 int 指標轉換為 void 指標
這在與使用 void 指標的原生元件交互操作時很有幫助。
match!
關鍵字
match!
關鍵字會在計算運算式內增強模式比對:
// 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
}
這可讓您縮短通常牽涉到混合選項 (或其他型別) 與非同步運算式等計算運算式的程式碼。 如需詳細資訊,請參閱 match!。
陣列、清單和序列運算式中的寬鬆向上轉型需求
混合型別,其中某個型別可能繼承自陣列、清單和序列運算式內的另一個型別,傳統上會要求您使用 :>
或 upcast
將任何衍生型別向上轉型為其父型別。 現在已寬鬆,如下所示:
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
陣列和清單運算式的縮排寬鬆
在 F# 4.5 之前,您必須在當做引數傳遞至方法呼叫時,過度縮排陣列和清單運算式。 已不再是必要項目:
module NoExcessiveIndenting =
System.Console.WriteLine(format="{0}", arg = [|
"hello"
|])
System.Console.WriteLine([|
"hello"
|])
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應