轉型和轉換 (F#)
本主題描述 F# 對型別轉換的支援。
算術型別
F# 提供用於各種基本型別之間 (例如整數和浮點型別之間) 算術轉換的轉換運算子。 整數和字元轉換運算子都有已檢查和未檢查形式,而浮點運算子和 enum 轉換運算子則沒有。 未檢查形式是在 Microsoft.FSharp.Core.Operators 中定義,而已檢查形式則是在 Microsoft.FSharp.Core.Operators.Checked 中定義。 檢查形式會檢查溢位,如果結果值超出目標型別的限制,則會產生執行階段例外狀況。
上述每個運算子的名稱都與目的型別的名稱相同。 例如,在下列程式碼中,型別已明確標註,byte 會有兩個不同的意義。 第一次出現的 byte 為型別,第二次出現的 byte 則為轉換運算子。
let x : int = 5
let b : byte = byte x
下表顯示 F# 中所定義的轉換運算子。
運算子 |
描述 |
---|---|
byte |
轉換為位元組,8 位元不帶正負號的型別。 |
sbyte |
轉換為帶正負號的位元組。 |
int16 |
轉換為 16 位元帶正負號的整數。 |
uint16 |
轉換為 16 位元不帶正負號的整數。 |
int32, int |
轉換為 32 位元帶正負號的整數。 |
uint32 |
轉換為 32 位元不帶正負號的整數。 |
int64 |
轉換為 64 位元帶正負號的整數。 |
uint64 |
轉換為 64 位元不帶正負號的整數。 |
nativeint |
轉換為原生整數。 |
unativeint |
轉換為不帶正負號的原生整數。 |
float, double |
轉換為 64 位元雙精確度 IEEE 浮點數。 |
float32, single |
轉換為 32 位元單精確度 IEEE 浮點數。 |
decimal |
轉換為 System.Decimal。 |
char |
轉換為 System.Char,這是 Unicode 字元。 |
enum |
轉換為列舉型別。 |
除了內建的基本型別之外,您還可以透過適當的簽章,來搭配使用這些運算子與實作 op_Explicit 或 op_Implicit 方法的型別。 例如,int 轉換運算子可以使用任何型別,而此型別提供會採用型別做為參數並傳回 int 的靜態方法 op_Explicit。 由於方法無法由傳回型別多載這個一般規則的特殊例外狀況,您可以針對 op_Explicit 和 op_Implicit 這樣做。
列舉型別
enum 運算子是泛型運算子,會接受一個型別參數,這個參數表示轉換的目標 enum 型別。 當它轉換為列舉型別時,型別推斷會嘗試判斷要轉換為 enum 的型別為何。 在下列範例中,未明確標註變數 col1,但是會從後面的相等測試來推斷它的型別。 因此,編譯器會推斷您是要轉換為 Color 列舉。 或者,您也可以提供型別附註,如下列範例中的 col2 所示。
type Color =
| Red = 1
| Green = 2
| Blue = 3
// The target type of the conversion is determined by type inference.
let col1 = enum 1
// The target type is supplied by a type annotation.
let col2 : Color = enum 2
do
if (col1 = Color.Red) then
printfn "Red"
您也可以明確指定目標的列舉別做為型別參數,下列程式碼所示:
let col3 = enum<Color> 3
請注意,列舉轉換工作,只有列舉的基礎型別是相容於所轉換的型別。 在下列程式碼中,轉換失敗進行編譯,因為不相配的int32和uint32。
// Error: types are incompatible
let col4 : Color = enum 2u
如需詳細資訊,請參閱 列舉 (F#)。
物件型別轉型
物件階層架構中的型別轉換是物件導向程式設計的基本。 有兩種基本轉換:向上轉型 (Upcasting) 和向下轉型 (Downcasting)。 在階層中向上轉型表示從衍生的物件參考轉型為基底物件參考。 只要基底類別是在衍生類別的繼承階層中,這類轉型保證會成功。 在階層中向下轉型表示從基底物件參考轉型為衍生的物件參考。只有在物件實際上是正確目的 (衍生) 型別的執行個體,或衍生自目的型別之型別的執行個體時,向下轉型才會成功。
F# 提供這些轉換類型的運算子。 :> 運算子會在階層中向上轉型,而 :?> 運算子則會在階層中向下轉型。
向上轉型
在許多物件導向語言中,向上轉型為隱含,而在 F# 中,規則稍微不同。 當您將引數傳遞至物件型別上的方法時,會自動套用向上轉型。 不過,對於模組中使用 let 繫結的函式,除非參數型別宣告為彈性型別,否則不會自動套用向上轉型。 如需詳細資訊,請參閱彈性型別 (F#)。
:> 運算子會執行靜態轉型,這表示是在編譯時期決定轉型是否成功。 如果使用 :> 的轉型成功編譯,它就是有效轉型,因此在執行階段不可能失敗。
您也可以使用 upcast 運算子來執行這類轉換。 下列運算式會指定在階層中向上轉換。
upcast expression
當您使用向上轉型運算子時,編譯器會嘗試從內容中推斷轉換的目標型別。 如果編譯器無法判斷目標型別,就會回報錯誤。
向下轉型
:?> 運算子會執行動態轉型,這表示是在執行階段決定轉型是否成功。 在編譯時期不會檢查使用 :?> 運算子的轉型,但是在執行階段則會嘗試轉型為指定的型別。 如果物件與目標型別相容,轉型便會成功。 如果物件與目標型別不相容,執行階段會引發 InvalidCastException。
您也可以使用 downcast 運算子來執行動態型別轉換。 下列運算式會指定在階層中向下轉換為從程式內容推斷的型別。
downcast expression
如同向上轉型運算子,如果編譯器無法從內容推斷特定的目標型別,就會回報錯誤。
在下列程式碼中,會示範 :> 和 :?> 運算子的用法。 其中說明當您知道轉換會成功時最適合使用 :?> 運算子,因為它會在轉換失敗時擲回 InvalidCastException。 如果您不知道轉換是否會成功,則使用 match 運算式的型別測試會是較好的選擇,因為它會避免產生例外狀況的額外負荷。
type Base1() =
abstract member F : unit -> unit
default u.F() =
printfn "F Base1"
type Derived1() =
inherit Base1()
override u.F() =
printfn "F Derived1"
let d1 : Derived1 = Derived1()
// Upcast to Base1.
let base1 = d1 :> Base1
// This might throw an exception, unless
// you are sure that base1 is really a Derived1 object, as
// is the case here.
let derived1 = base1 :?> Derived1
// If you cannot be sure that b1 is a Derived1 object,
// use a type test, as follows:
let downcastBase1 (b1 : Base1) =
match b1 with
| :? Derived1 as derived1 -> derived1.F()
| _ -> ()
downcastBase1 base1
因為泛型運算子 downcast 和 upcast 是依賴型別推斷來判斷引數和傳回型別,所以在上述程式碼中,您可以將
let base1 = d1 :> Base1
with
base1 = upcast d1
在上述程式碼中,引數型別和傳回型別分別是 Derived1 和 Base1。
如需型別測試的詳細資訊,請參閱搜尋運算式 (F#)。