2024年5月22日 星期三

5.VB.NET 精進篇 筆記 - 數據類型轉換

VB.NET 數據類型轉換筆記(精進篇)

VB.NET 數據類型轉換 筆記(精進篇)

VB.NET 程式常會遇到資料型別不一致的情況。例如表單輸入是文字,但計算需要數字;資料庫欄位是日期,但畫面需要格式化文字;二進位封包是位元組陣列,但程式需要還原成整數。

型別轉換的重點不只是「能不能轉」,而是轉換後是否安全、是否會遺失資料、失敗時是否能保留流程。這篇整理常見轉換方式,包含 CTypeDirectCastTryCastParseTryParseParseExactBitConverter、裝箱拆箱與自訂轉換。

型別轉換的核心觀念

先判斷轉換風險

型別轉換可先分成兩個方向:一種是比較安全的擴大型轉換,另一種是可能有風險的縮小型轉換。

  • 擴大型轉換:目標型別容量較大,來源值通常能完整放入,例如 Integer 轉成 Long
  • 縮小型轉換:目標型別容量較小,或無法完整保存小數與精度,例如 Decimal 轉成 Integer
  • 外部資料轉換:來源可能格式錯誤,例如表單輸入、CSV、API、檔案內容,通常需要使用安全判斷。

轉換前建議檢查三件事:

  1. 範圍:目標型別是否足以容納來源值。
  2. 精度:小數位、有效數字或日期格式是否會改變。
  3. 失敗處理:格式錯誤、溢位或型別不相容時,是否需要避免程式中斷。
判斷面向 風險較低 風險較高
數值範圍 目標型別範圍更大。 目標型別範圍更小,可能溢位。
資料精度 位數與小數可完整保留。 可能發生捨入、截斷或格式改變。
資料來源 程式內部固定資料。 表單輸入、檔案、API、使用者貼上內容。
失敗處理 失敗機率低,可直接轉換。 失敗機率高,應優先使用 TryParse 或 TryCast。

執行環境與範例形式

Windows Forms 物件配置

以下範例以 Windows Forms 為主。每個場景都保留完整範例程式碼,方便直接建立測試。

  • Button1:執行轉換範例。
  • TextBox1:輸入要轉換的文字資料。
  • Label1:顯示單一或多行轉換結果。
  • ListBox1:顯示多筆輸出,例如位元組陣列內容。

內建數值型別轉換

場景一:擴大型轉換 Integer → Long

當來源型別可完整放入目標型別時,通常屬於擴大型轉換。例如 Integer 是 32 位元整數,Long 是 64 位元整數,Long 可容納更大的整數範圍。

範例程式碼
VB.NET / Windows Forms
Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim monthlyOrderCount As Integer = 48215
        Dim annualOrderCount As Long = monthlyOrderCount

        Label1.Text = "原始型別:" & monthlyOrderCount.GetType().Name & vbCrLf &
                      "轉換後型別:" & annualOrderCount.GetType().Name & vbCrLf &
                      "轉換後值:" & annualOrderCount.ToString("N0")
    End Sub
End Class
畫面輸出結果(Label1.Text)
原始型別:Int32 轉換後型別:Int64 轉換後值:48,215
邏輯解析
  • Integer 對應執行階段型別 Int32
  • Long 對應執行階段型別 Int64
  • 較小範圍的整數放入較大範圍的整數,資料不會遺失。
  • 這類轉換通常可直接指定,不需要額外寫 CType

場景二:縮小型轉換 Decimal → Integer

Decimal 轉成 Integer 時,小數部分無法原樣保留,因此屬於縮小型轉換。這類轉換應明確寫出來,避免看不出資料可能被改變。

範例程式碼
VB.NET / Windows Forms
Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim packageWeight As Decimal = 13.75D
        Dim roundedWeight As Integer = CType(packageWeight, Integer)

        Label1.Text = "原始 Decimal:" & packageWeight.ToString() & vbCrLf &
                      "轉換後 Integer:" & roundedWeight.ToString()
    End Sub
End Class
畫面輸出結果(Label1.Text)
原始 Decimal:13.75 轉換後 Integer:14
邏輯解析
  • CType(packageWeight, Integer) 明確表示此處發生型別轉換。
  • Decimal 可保存小數,Integer 只能保存整數。
  • 轉成整數後,小數精度不會保留。
  • 涉及重量、金額、測量值時,應特別留意四捨五入或精度遺失是否符合需求。

縮小型轉換的常見風險

縮小型轉換不只可能改變小數,也可能因來源值超過目標型別範圍而產生例外。金額、小數、統計值、外部匯入資料都應明確處理轉換規則。

參考型別轉換:DirectCast 與 TryCast

場景三:已確定實際型別時使用 DirectCast

DirectCast 適合用在實際型別已經確定的情境。若實際物件確實是目標型別,就能轉換成功;若不是,會直接產生 InvalidCastException

範例程式碼
VB.NET / Windows Forms
Public Class NotificationChannel
    Public Property ChannelName As String
End Class

Public Class SmsChannel
    Inherits NotificationChannel

    Public Property PhoneNumber As String
End Class

Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim baseChannel As NotificationChannel = New SmsChannel With {
            .ChannelName = "系統簡訊",
            .PhoneNumber = "0900-123-456"
        }

        Dim sms As SmsChannel = DirectCast(baseChannel, SmsChannel)

        Label1.Text = "通道名稱:" & sms.ChannelName & vbCrLf &
                      "接收號碼:" & sms.PhoneNumber
    End Sub
End Class
畫面輸出結果(Label1.Text)
通道名稱:系統簡訊 接收號碼:0900-123-456
邏輯解析
  • baseChannel 的宣告型別是 NotificationChannel
  • 實際建立的物件是 SmsChannel,因此可轉回 SmsChannel
  • 轉換成功後,可讀取 SmsChannel 才有的 PhoneNumber 屬性。
  • 若實際物件不是 SmsChannelDirectCast 會產生例外。

場景四:不確定型別時使用 TryCast

TryCast 適合用在成功與否不確定的參考型別轉換。轉換失敗時不會丟出例外,而是回傳 Nothing

範例程式碼
VB.NET / Windows Forms
Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim rawControl As Object = Label1
        Dim inputBox As TextBox = TryCast(rawControl, TextBox)

        If inputBox Is Nothing Then
            Label1.Text = "TryCast 失敗:來源物件不是 TextBox。"
        Else
            Label1.Text = "TextBox 內容:" & inputBox.Text
        End If
    End Sub
End Class
畫面輸出結果(Label1.Text)
TryCast 失敗:來源物件不是 TextBox。
邏輯解析
  • rawControl 實際指向的是 Label1
  • TryCast(rawControl, TextBox) 嘗試把它轉成 TextBox
  • 因為實際型別不符合,所以 inputBox 會是 Nothing
  • TryCast 只能用於參考型別,不能用來轉換 IntegerDecimal 這類值型別。

字串與數值的轉換

場景五:使用 Integer.Parse 解析固定格式資料

Parse 適合資料格式已確定的情況。例如內部設定、固定格式檔案、已驗證過的欄位。若文字格式錯誤,Parse 會直接產生例外。

範例程式碼
VB.NET / Windows Forms
Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        TextBox1.Text = "256"

        Dim quantityText As String = TextBox1.Text.Trim()
        Dim quantity As Integer = Integer.Parse(quantityText)

        Label1.Text = "解析成功,數值 = " & quantity.ToString()
    End Sub
End Class
畫面輸出結果(TextBox1 = 256)
解析成功,數值 = 256
邏輯解析
  • Integer.Parse 會把文字轉成整數。
  • 本例的 TextBox1.Text 是固定填入的 256,格式可預期。
  • 若輸入變成 7A5Parse 會產生格式錯誤例外。
  • 自由輸入欄位通常不建議直接使用 Parse

場景六:使用 Integer.TryParse 處理表單輸入

TryParse 適合處理不穩定來源,例如表單輸入、匯入檔、貼上內容與外部資料。轉換成功時回傳 True,失敗時回傳 False,流程不會被例外中斷。

範例程式碼
VB.NET / Windows Forms
Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim stockText As String = TextBox1.Text.Trim()
        Dim stockValue As Integer

        If Integer.TryParse(stockText, stockValue) Then
            Label1.Text = "庫存數量:" & stockValue.ToString("N0")
        Else
            Label1.Text = "輸入無法轉成整數,請檢查是否含有非數字字元。"
        End If
    End Sub
End Class
畫面輸出結果(TextBox1 = 7A5)
輸入無法轉成整數,請檢查是否含有非數字字元。
邏輯解析
  • Integer.TryParse(stockText, stockValue) 會嘗試把文字轉成整數。
  • 成功時回傳 True,並把結果放入 stockValue
  • 失敗時回傳 False,程式可顯示錯誤訊息,而不是直接中斷。
  • 表單輸入與外部資料通常優先使用 TryParse

Parse 與 TryParse 的差異

Parse 適合資料應該正確的情境;TryParse 適合資料可能錯誤的情境。資料來源越靠近外部輸入,越應優先使用 TryParse

日期時間轉換

場景七:使用 Date.TryParseExact 解析固定日期格式

日期文字若來自報表、交換檔或跨系統傳輸,不應完全依賴系統地區設定自動判讀。固定格式搭配 TryParseExact 可同時保留格式嚴謹與失敗防護。

範例程式碼
VB.NET / Windows Forms
Imports System.Globalization

Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim sourceText As String = TextBox1.Text.Trim()
        Dim confirmedDate As Date

        If Date.TryParseExact(sourceText,
                              "yyyy-MM-dd HH:mm:ss",
                              CultureInfo.InvariantCulture,
                              DateTimeStyles.None,
                              confirmedDate) Then
            Label1.Text = "解析成功:" & confirmedDate.ToString("yyyy/MM/dd HH:mm:ss")
        Else
            Label1.Text = "日期格式不正確,請輸入 yyyy-MM-dd HH:mm:ss"
        End If
    End Sub
End Class
畫面輸出結果(TextBox1 = 2026-06-15 14:20:30)
解析成功:2026/06/15 14:20:30
邏輯解析
  • TryParseExact 要求來源文字符合指定格式。
  • yyyy-MM-dd HH:mm:ss 代表來源必須像 2026-06-15 14:20:30
  • CultureInfo.InvariantCulture 可降低不同地區設定造成的解析差異。
  • 解析失敗時不會丟出例外,而是進入 Else 顯示提示。

位元組陣列與基本型別轉換

場景八:使用 BitConverter 將 Int32 轉成 Byte()

二進位檔案、設備通訊、封包組合與序列化過程,常需要把數值轉成位元組陣列。BitConverter.GetBytes 可將基本型別轉成 Byte()

範例程式碼
VB.NET / Windows Forms
Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim sensorValue As Integer = 1024
        Dim packetBytes As Byte() = BitConverter.GetBytes(sensorValue)

        ListBox1.Items.Clear()
        ListBox1.Items.Add("原始整數:" & sensorValue.ToString())
        ListBox1.Items.Add("位元組序列:" & BitConverter.ToString(packetBytes))
        ListBox1.Items.Add("陣列長度:" & packetBytes.Length.ToString())
        ListBox1.Items.Add("系統是否為小端序:" & BitConverter.IsLittleEndian.ToString())
    End Sub
End Class
畫面輸出結果(ListBox1)
原始整數:1024 位元組序列:00-04-00-00 陣列長度:4 系統是否為小端序:True
邏輯解析
  • Integer 是 32 位元整數,因此轉成 Byte() 後長度為 4。
  • BitConverter.ToString(packetBytes) 可把位元組陣列轉成易讀的十六進位字串。
  • 輸出順序與系統大小端序有關。
  • 與外部設備或封包規格交換資料時,需確認對方使用的位元組順序。

場景九:將 Byte() 還原回 Int32

接收封包或檔案資料後,若位元組排列與格式正確,可透過 BitConverter.ToInt32 還原整數。

範例程式碼
VB.NET / Windows Forms
Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim incomingBytes As Byte() = {44, 1, 0, 0}
        Dim restoredNumber As Integer = BitConverter.ToInt32(incomingBytes, 0)

        Label1.Text = "來源位元組:" & BitConverter.ToString(incomingBytes) & vbCrLf &
                      "還原數值:" & restoredNumber.ToString()
    End Sub
End Class
畫面輸出結果(Label1.Text)
來源位元組:2C-01-00-00 還原數值:300
邏輯解析
  • ToInt32(incomingBytes, 0) 表示從索引 0 開始讀取 4 個位元組。
  • 2C-01-00-00 在小端序環境中會還原為 300。
  • 若起始位置錯誤或位元組長度不足,還原結果會錯誤或產生例外。
  • 處理封包時,位移位置通常需依照規格文件管理。

裝箱與拆箱

場景十:值型別存入 Object,再還原成原型別

值型別放入 Object 時會發生裝箱。之後若要取回原本的值型別,就會發生拆箱。此類情境常見於舊版 API、通用集合或需要以 Object 暫存資料的流程。

範例程式碼
VB.NET / Windows Forms
Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim originalScore As Integer = 88
        Dim boxedValue As Object = originalScore
        Dim restoredScore As Integer = CType(boxedValue, Integer)

        Label1.Text = "原始值:" & originalScore.ToString() & vbCrLf &
                      "裝箱後型別:" & boxedValue.GetType().Name & vbCrLf &
                      "拆箱後值:" & restoredScore.ToString()
    End Sub
End Class
畫面輸出結果(Label1.Text)
原始值:88 裝箱後型別:Int32 拆箱後值:88
邏輯解析
  • Integer 是值型別,放入 Object 時會被裝箱。
  • boxedValue.GetType().Name 仍顯示實際內容是 Int32
  • CType(boxedValue, Integer) 會把裝箱後的值還原成 Integer
  • 拆箱時型別必須正確,否則可能產生轉換例外。

自訂型別轉換

場景十一:為自訂類別建立 CType 轉換邏輯

若某個類別本身代表明確的數值概念,可把轉換規則放進類別內部。例如儲存空間配額可由整數建立,也可轉回整數顯示。

範例程式碼
VB.NET / Windows Forms
Public Class StorageQuota
    Public Property CapacityInGB As Integer

    Public Shared Widening Operator CType(value As Integer) As StorageQuota
        Return New StorageQuota With {.CapacityInGB = value}
    End Operator

    Public Shared Narrowing Operator CType(quota As StorageQuota) As Integer
        Return quota.CapacityInGB
    End Operator
End Class

Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim quota As StorageQuota = 250
        Dim rawValue As Integer = CType(quota, Integer)

        Label1.Text = "配額物件:" & quota.CapacityInGB.ToString() & " GB" & vbCrLf &
                      "轉回整數:" & rawValue.ToString() & " GB"
    End Sub
End Class
畫面輸出結果(Label1.Text)
配額物件:250 GB 轉回整數:250 GB
邏輯解析
  • Widening Operator CType 定義由 Integer 建立 StorageQuota 的規則。
  • Narrowing Operator CType 定義由 StorageQuota 轉回 Integer 的規則。
  • Dim quota As StorageQuota = 250 可成立,是因為類別定義了整數到物件的轉換。
  • 自訂轉換可提升語意,但規則必須直觀,否則會降低程式可讀性。

實務選用整理

情境 建議方法 說明
安全擴大型轉換 直接指定 資料範圍可完整保留時,通常不需額外語法。
縮小型轉換 CType 明確表達可能存在精度變化或例外風險。
已知參考型別 DirectCast 實際型別確定時可使用;錯誤時會產生例外。
不確定參考型別 TryCast 失敗時回傳 Nothing,可避免流程中斷。
固定合法字串 Parse 適合可控來源,不適合自由輸入。
表單或外部輸入 TryParse 失敗時可顯示提示,流程仍能繼續。
固定日期格式 TryParseExact 可同時控制格式並避免例外中斷。
二進位資料 BitConverter 適合基本型別與位元組陣列互轉,但需注意大小端序。
  1. 型別轉換前應先判斷範圍、精度與失敗風險。
  2. 擴大型轉換通常較安全;縮小型轉換應明確寫出轉換語法。
  3. 表單輸入、檔案、API 等外部資料,通常優先使用 TryParse
  4. 參考型別已確定時可用 DirectCast;不確定時可用 TryCast
  5. 日期文字若格式固定,使用 TryParseExact 比自動解析更穩定。
  6. BitConverter 適合處理封包與二進位資料,但需留意位元組順序。
  7. 自訂轉換能提升語意,但規則必須直觀,避免造成隱藏邏輯。