VB.NET 建構式(Constructor) 筆記(進階篇)
Constructor 是物件建立時自動執行的初始化程序。在 VB.NET 中,建構式寫成 Sub New。它不只是用來填預設值,更重要的是讓物件一建立就符合必要規則,避免產生半完成、資料不完整或狀態不合理的物件。
建構式進階篇的重點,是理解不同初始化方式如何安排:何時使用必要參數、何時使用重載、何時用 Optional、何時讓建構式呼叫另一個建構式,以及繼承時為什麼要先呼叫 MyBase.New。
先理解建構式的角色
建構式是物件建立時的入口
只要程式寫下 New 類別名稱(...),就會進入該類別對應的 Sub New。因此,建構式很適合放「物件一開始就必須成立」的規則。
- 設定初始值:例如狀態、編號、預設數量。
- 接收必要資料:例如名稱、代碼、容量、起始金額。
- 檢查資料合理性:例如數量不可小於 0、名稱不可空白。
- 串接父類別初始化:繼承時先完成基底類別需要的資料。
建構式適合處理的事情:
- 必要資料:缺少就不應該建立物件。
- 初始狀態:建立後馬上要能正常使用。
- 固定規則:不希望外部到處重複寫同樣初始化邏輯。
基本語法
VB.NET
Public Class 類別名稱
Public Sub New(ByVal 參數名稱 As 資料型別)
' 建立物件時要執行的初始化流程
End Sub
End Class
預設建構式與必要參數
場景一:建立取餐叫號卡
這個範例用取餐叫號卡示範兩種建立方式:沒有參數時建立「尚未發號」狀態;有參數時建立可顯示的取餐卡。
需要的主控項
ButtonCreateEmpty:建立尚未發號的叫號卡。ButtonCreateTicket:建立正式叫號卡。LabelTicket:顯示叫號卡內容。
範例程式碼
VB.NET / Windows Forms
Public Class PickupTicket
Public Property TicketNo As String
Public Property CounterName As String
Public Property IsReady As Boolean
Public Sub New()
TicketNo = "尚未發號"
CounterName = "未分配櫃台"
IsReady = False
End Sub
Public Sub New(ByVal ticketNo As String, ByVal counterName As String)
Me.TicketNo = ticketNo
Me.CounterName = counterName
Me.IsReady = True
End Sub
Public Function BuildText() As String
Dim readyText As String = If(IsReady, "可取餐", "等待發號")
Return TicketNo & "|" & CounterName & "|" & readyText
End Function
End Class
Public Class Form1
Private Sub ButtonCreateEmpty_Click(sender As Object, e As EventArgs) Handles ButtonCreateEmpty.Click
Dim ticket As New PickupTicket()
LabelTicket.Text = ticket.BuildText()
End Sub
Private Sub ButtonCreateTicket_Click(sender As Object, e As EventArgs) Handles ButtonCreateTicket.Click
Dim ticket As New PickupTicket("B-18", "二號櫃台")
LabelTicket.Text = ticket.BuildText()
End Sub
End Class
畫面輸出結果(ButtonCreateTicket)
B-18|二號櫃台|可取餐邏輯解析
Public Sub New()是無參數建構式,用來建立預設狀態。Public Sub New(ticketNo, counterName)是帶參數建構式,用來建立完整叫號卡。- 不同建構式代表不同初始化路徑,但建立出來都是
PickupTicket物件。
建構式重載與 Me.New
場景二:建立展場通行貼紙
建構式重載可以提供多種建立方式。若多個建構式有共用邏輯,可以用 Me.New(...) 呼叫同一個主要建構式,避免初始化規則分散。
需要的主控項
ButtonCreateVisitor:建立一般訪客貼紙。ButtonCreateStaff:建立工作人員貼紙。LabelBadge:顯示貼紙內容。
範例程式碼
VB.NET / Windows Forms
Public Class ExpoBadge
Public Property DisplayName As String
Public Property BadgeType As String
Public Property CanEnterBackstage As Boolean
Public Sub New(ByVal displayName As String)
Me.New(displayName, "一般參觀", False)
End Sub
Public Sub New(ByVal displayName As String,
ByVal badgeType As String,
ByVal canEnterBackstage As Boolean)
If displayName.Trim() = String.Empty Then
Throw New ArgumentException("顯示名稱不可空白。")
End If
Me.DisplayName = displayName
Me.BadgeType = badgeType
Me.CanEnterBackstage = canEnterBackstage
End Sub
Public Function BuildText() As String
Dim backstageText As String = If(CanEnterBackstage, "可進後台", "不可進後台")
Return DisplayName & "|" & BadgeType & "|" & backstageText
End Function
End Class
Public Class Form1
Private Sub ButtonCreateVisitor_Click(sender As Object, e As EventArgs) Handles ButtonCreateVisitor.Click
Dim badge As New ExpoBadge("來賓 042")
LabelBadge.Text = badge.BuildText()
End Sub
Private Sub ButtonCreateStaff_Click(sender As Object, e As EventArgs) Handles ButtonCreateStaff.Click
Dim badge As New ExpoBadge("場務小組", "工作人員", True)
LabelBadge.Text = badge.BuildText()
End Sub
End Class
畫面輸出結果(ButtonCreateStaff)
場務小組|工作人員|可進後台邏輯解析
- 單參數建構式用
Me.New(...)呼叫三參數建構式。 - 驗證規則集中在主要建構式中,不必重複寫兩次。
Me.New(...)必須放在建構式的第一行。
Optional 參數與初始化驗證
場景三:建立閱讀活動預約資料
Optional 適合用在少量、單純的可選初始值。這個範例中,讀者姓名與場次代碼是必要資料,座位區與是否需要耳機則有預設值。
需要的主控項
TextBoxReaderName:輸入讀者姓名。TextBoxSessionCode:輸入場次代碼。ButtonReserve:建立預約。LabelReservation:顯示預約結果。
範例程式碼
VB.NET / Windows Forms
Public Class ReadingReservation
Public Property ReaderName As String
Public Property SessionCode As String
Public Property SeatArea As String
Public Property NeedHeadset As Boolean
Public Sub New(ByVal readerName As String,
ByVal sessionCode As String,
Optional ByVal seatArea As String = "自由座",
Optional ByVal needHeadset As Boolean = False)
If readerName.Trim() = String.Empty Then
Throw New ArgumentException("讀者姓名不可空白。")
End If
If sessionCode.Trim() = String.Empty Then
Throw New ArgumentException("場次代碼不可空白。")
End If
Me.ReaderName = readerName
Me.SessionCode = sessionCode
Me.SeatArea = seatArea
Me.NeedHeadset = needHeadset
End Sub
Public Function BuildText() As String
Dim headsetText As String = If(NeedHeadset, "需要耳機", "不需耳機")
Return ReaderName & "|" & SessionCode & "|" & SeatArea & "|" & headsetText
End Function
End Class
Public Class Form1
Private Sub ButtonReserve_Click(sender As Object, e As EventArgs) Handles ButtonReserve.Click
Try
Dim reservation As New ReadingReservation(
TextBoxReaderName.Text.Trim(),
TextBoxSessionCode.Text.Trim())
LabelReservation.Text = reservation.BuildText()
Catch ex As ArgumentException
LabelReservation.Text = ex.Message
End Try
End Sub
End Class
畫面輸出結果(未指定 Optional 參數)
林同學|R-204|自由座|不需耳機邏輯解析
readerName與sessionCode是必要資料。seatArea與needHeadset有預設值,可不傳入。- 必要資料驗證放在建構式中,能避免建立出不完整的預約物件。
Optional 不適合包辦所有情境。 若參數越來越多,或不同組合代表不同建立流程,建構式重載、Factory Method 或獨立設定物件通常會比一長串 Optional 參數清楚。
建構式與物件初始化器
場景四:建立報表匯出工作
建構式適合放必要資料,物件初始化器適合設定可選屬性。這樣可以讓物件建立時先具備最低可用狀態,再依需求補上額外設定。
需要的主控項
ButtonCreateJob:建立匯出工作。LabelJob:顯示工作設定。
範例程式碼
VB.NET / Windows Forms
Public Class ExportJob
Public ReadOnly Property JobName As String
Public Property FileName As String
Public Property IncludeTimestamp As Boolean
Public Property IsCompressed As Boolean
Public Sub New(ByVal jobName As String)
If jobName.Trim() = String.Empty Then
Throw New ArgumentException("工作名稱不可空白。")
End If
Me.JobName = jobName
Me.FileName = "export.txt"
Me.IncludeTimestamp = True
Me.IsCompressed = False
End Sub
Public Function BuildText() As String
Return JobName & "|" & FileName & "|壓縮:" & IsCompressed.ToString()
End Function
End Class
Public Class Form1
Private Sub ButtonCreateJob_Click(sender As Object, e As EventArgs) Handles ButtonCreateJob.Click
Dim job As New ExportJob("每日摘要") With {
.FileName = "daily-summary.csv",
.IsCompressed = True
}
LabelJob.Text = job.BuildText()
End Sub
End Class
畫面輸出結果(LabelJob.Text)
每日摘要|daily-summary.csv|壓縮:True邏輯解析
JobName是必要資料,因此放在建構式。FileName、IsCompressed是可選設定,因此用物件初始化器補上。- 建構式與初始化器不是互斥,而是分別處理必要與可選資料。
Private 建構式與建立方法
場景五:統一產生交班批次代碼
有些物件不希望外部隨便 New,因為建立時需要固定格式或統一規則。這時可以把建構式設為 Private,再提供公開的建立方法。
需要的主控項
ButtonCreateBatch:建立交班批次。LabelBatch:顯示批次代碼。
範例程式碼
VB.NET / Windows Forms
Public Class ShiftBatch
Public ReadOnly Property BatchCode As String
Public ReadOnly Property CreatedAt As DateTime
Private Sub New(ByVal batchCode As String, ByVal createdAt As DateTime)
Me.BatchCode = batchCode
Me.CreatedAt = createdAt
End Sub
Public Shared Function CreateMorningBatch(ByVal runningNo As Integer) As ShiftBatch
If runningNo <= 0 Then
Throw New ArgumentException("流水號必須大於 0。")
End If
Dim code As String = "M-" & Date.Today.ToString("MMdd") & "-" & runningNo.ToString("000")
Return New ShiftBatch(code, DateTime.Now)
End Function
Public Function BuildText() As String
Return BatchCode & "|建立時間:" & CreatedAt.ToString("HH:mm:ss")
End Function
End Class
Public Class Form1
Private Sub ButtonCreateBatch_Click(sender As Object, e As EventArgs) Handles ButtonCreateBatch.Click
Dim batch As ShiftBatch = ShiftBatch.CreateMorningBatch(7)
LabelBatch.Text = batch.BuildText()
End Sub
End Class
畫面輸出結果(LabelBatch.Text)
M-0425-007|建立時間:09:30:12邏輯解析
Private Sub New讓外部不能直接任意建立物件。CreateMorningBatch集中建立規則,統一批次代碼格式。Shared Function可在不用先建立物件的情況下呼叫。
建構式與繼承
場景六:一般提醒與倒數提醒
繼承時,子類別建立之前必須先完成父類別的初始化。若父類別建構式需要參數,子類別通常要使用 MyBase.New(...) 明確傳入。
需要的主控項
ButtonShowNotice:建立提醒物件。ListBoxNotice:顯示提醒內容。
範例程式碼
VB.NET / Windows Forms
Public Class NoticeBase
Public ReadOnly Property Title As String
Public Sub New(ByVal title As String)
If title.Trim() = String.Empty Then
Throw New ArgumentException("提醒標題不可空白。")
End If
Me.Title = title
End Sub
Public Overridable Function BuildText() As String
Return "提醒:" & Title
End Function
End Class
Public Class CountdownNotice
Inherits NoticeBase
Public ReadOnly Property MinutesLeft As Integer
Public Sub New(ByVal title As String, ByVal minutesLeft As Integer)
MyBase.New(title)
If minutesLeft < 0 Then
Throw New ArgumentException("剩餘分鐘不可小於 0。")
End If
Me.MinutesLeft = minutesLeft
End Sub
Public Overrides Function BuildText() As String
Return "倒數 " & MinutesLeft.ToString() & " 分鐘|" & Title
End Function
End Class
Public Class Form1
Private Sub ButtonShowNotice_Click(sender As Object, e As EventArgs) Handles ButtonShowNotice.Click
Dim notices As New List(Of NoticeBase) From {
New NoticeBase("補上會議記錄"),
New CountdownNotice("線上課程開始", 10)
}
ListBoxNotice.Items.Clear()
For Each item As NoticeBase In notices
ListBoxNotice.Items.Add(item.BuildText())
Next
End Sub
End Class
畫面輸出結果(ListBoxNotice)
提醒:補上會議記錄
倒數 10 分鐘|線上課程開始邏輯解析
NoticeBase的建構式負責檢查並設定標題。CountdownNotice使用MyBase.New(title)先完成父類別初始化。- 子類別再設定自己的
MinutesLeft。 - 初始化順序是先父類別,再子類別。
實務判斷與常見誤區
常見問題整理
- 建構式做太多事:把大量檔案讀寫、資料庫連線或網路等待放進建構式,會讓物件建立過程難以控制。
- 必要資料沒有放進建構式:先建立空物件再到處補資料,容易產生半完成狀態。
- 重載過多:建構式版本太多時,外部反而難以判斷該使用哪一個。
- Optional 過長:參數太多、規則太多時,應考慮改用設定物件或建立方法。
- 繼承時忘記父類別初始化:父類別沒有無參數建構式時,子類別需明確呼叫
MyBase.New。
| 做法 | 適合情境 | 注意事項 |
|---|---|---|
| 必要參數建構式 | 資料缺少就不應建立物件。 | 參數不要過多。 |
| 建構式重載 | 同一物件有少數幾種明確建立方式。 | 可用 Me.New 集中共同規則。 |
| Optional 參數 | 只有少數可選設定。 | 過多會降低可讀性。 |
| 物件初始化器 | 必要資料已完成,可再補可選屬性。 | 不適合處理必要規則。 |
| Private 建構式 | 建立規則需要集中控管。 | 通常搭配 Shared Function 建立物件。 |
重點整理
- 建構式在物件建立時自動執行,VB.NET 寫成
Sub New。 - 建構式適合設定必要初始值與檢查建立規則。
- 無參數建構式適合建立預設狀態;帶參數建構式適合要求必要資料。
- 建構式重載可提供多種建立方式,但不宜過度增加版本。
Me.New(...)可讓一個建構式呼叫另一個建構式,集中共同初始化規則。Optional適合少量可選初始值,不適合取代所有重載設計。- 物件初始化器適合補可選屬性,建構式適合保證必要規則。
- 繼承時,子類別可透過
MyBase.New(...)先完成父類別初始化。 - 建構式不適合放入太重、太慢或容易失敗的外部資源流程。