這是一份給實務開發者的 Excel VBA 變數地圖:用最少力氣避開最多坑。從 Option Explicit 的基本功開始,帶你釐清值型別與物件型別差異、作用域(程序/模組/全域/Static)與生命週期、ByRef/ByVal 的副作用,以及為何 Long 勝過 Integer、Currency/Decimal 怎麼選。
文中提供可直接套用的宣告與命名範例、Range/Worksheet 常見寫法,並整理轉型、字串/日期比較的眉角。最後列出最常見的錯誤與排除心法(Type mismatch、91、1004…),讓你寫得穩、改得快。希望本篇文章可以幫助到需要的您。
目錄
{tocify} $title={目錄}先講結論(實務守則)
1. 在每個模組最上面放 Option Explicit,並在 VB 編輯器勾選「Require Variable Declaration」。
2. 宣告時每個變數都要寫一次 As 型別:
Dim i As Long, n As Long(而不是 Dim i, n As Long)。
3. 以 Long 取代 Integer;少用 Variant,除非你真的需要可變型別或錯誤值。
4. 物件一定要用 Set 指派,結束時可視情況 Set obj = Nothing
(特別是外部 COM 物件)。
5. 參數預設是 ByRef,有副作用;不想改到外面就用 ByVal。
6. 針對 Excel:常用 Workbook / Worksheet / Range 做早期綁定(Early Binding),避免寫 Object。
7. 轉型用顯式函式(CLng、CDbl、CBool、CDate…),不要仰賴暗暗的自動轉型。
變數的三個核心:型別、作用域(Scope)、生命週期(Lifetime)
1. 型別(值型別 vs 物件)
值型別:Byte, Boolean, Long, Single, Double, Currency, String, Date, Variant 等。
Long(32 位)> Integer,在 VBA 內部算迴圈更快且不溢位。
Double:浮點,速度快但有誤差;Currency:定點小數,做金額更穩。
Date:其實是 Double(整數=日期,尾數=時間)。
Decimal:不是獨立型別,是 Variant 的子型別,只能 Dim v As Variant: v = CDec(123.45).
物件型別:Workbook, Worksheet, Range, Dictionary, Collection…
宣告 Dim ws As Worksheet,指派用 Set ws = ...。
釋放:Set ws = Nothing(Excel 內部物件通常不用強制,但外部 COM 物件建議釋放)。
2. 作用域
程序內變數:Dim x As Long 放在 Sub/Function 內,只在這個程序看得到。
模組層級:在「標準模組」最上方 Private mCounter As Long,整個模組可用。
全域:在標準模組最上方 Public AppName As String,整個專案可見。
Static:在程序內 Static hit As Long,變數值會在下次呼叫時保留。
3. 生命週期
程序內 Dim:跑完程序就釋放。
Static:專案仍開著就保留。
模組層級 / 全域:直到專案卸載(關閉 Excel 或重置 VBE)才釋放。
怎麼寫(宣告與命名示範)
Option Explicit ' 強制宣告
' 模組層級變數(Private:只在此模組可見)
Private mRunCount As Long
' 全域常數(放標準模組)
Public Const APP_NAME As String = "StockCleaner"
Sub DemoVariables()
' 值型別
Dim i As Long, total As Double
Dim isOk As Boolean
Dim when As Date
' Decimal 只能透過 Variant + CDec
Dim price As Variant
price = CDec(12345.67)
' 物件型別(Excel 常用)
Dim wb As Workbook, ws As Worksheet, rng As Range
Set wb = ThisWorkbook
Set ws = wb.Worksheets("Data")
Set rng = ws.Range("A1:A100")
' 字串
Dim name As String
name = "Alice"
' 計算
For i = 1 To rng.Rows.Count
total = total + CDbl(rng.Cells(i, 1).Value)
Next
' ByVal/ByRef 的差在於會不會改到外面的變數
isOk = CheckTotal(ByValValue:=total)
mRunCount = mRunCount + 1
End Sub
Private Function CheckTotal(ByVal ByValValue As Double) As Boolean
' 不會外溢修改呼叫端的變數
CheckTotal = (ByValValue > 0)
End Function
命名小撇步
物件前綴好辨識:wb、ws、rng、dic、col。
模組層級變數可加 m 前綴:mRunCount。
常數全大寫:APP_NAME、MAX_ROWS。
Excel 物件變數的日常
Sub SumRange()
Option Explicit
Dim ws As Worksheet
Dim rng As Range
Dim sumVal As Double
Set ws = ThisWorkbook.Worksheets("Sales")
Set rng = ws.Range("B2:B100")
' 盡量用 .Value2(更快、避免日期/貨幣自動轉換)
sumVal = Application.WorksheetFunction.Sum(rng.Value2)
MsgBox "Total: " & CStr(sumVal)
End Sub
Value2 比 Value 少了格式干擾,速度也更好。
WorksheetFunction 可能丟錯(#N/A…),可搭配 On Error Resume Next + 檢查,或自行迴圈。
轉型與比較(不要讓自動轉型扯你後腿)
顯式轉型:CLng, CDbl, CBool, CStr, CDate, CVar, CDec。
字串比較受 Option Compare 影響:
Option Compare Binary(預設,大小寫/排序嚴格)
Option Compare Text(不分大小寫)
Date 建議用 DateSerial / TimeSerial 建構,避免字串轉日期因地區格式踩雷。
Dim d As Date d = DateSerial(2025, 9, 21) + TimeSerial(14, 30, 0)
常見型別要點速查
Boolean:True = -1、False = 0。算術運算會跟著帶入 -1/0。
String:vbNullString 比 "" 更省記憶體(呼叫 API 時常用)。
Variant:
萬用,但慢、容易導致不預期的型別轉換。
可裝 Empty、Null、Error、Array。對 Error 用 IsError() 判。
Decimal(Variant 子型別):金額高精度,宣告要 Variant + CDec。
Currency:固定四位小數,金額計算穩、快。
Date:CLng(Date) 可取序號;Int(d) 取日期、d - Int(d) 取時間。
物件變數的典型雷點
1. 忘記 Set
Dim ws As Worksheet ws = ThisWorkbook.Worksheets(1) ' ❌ 會 Type mismatch Set ws = ThisWorkbook.Worksheets(1) ' ✅
2. Dim a, b As Long 只有 b 是 Long,a 是 Variant
Dim a As Long, b As Long ' ✅ 每個都寫型別
3. As New 的隱性成本(會在每次使用前檢查是否為 Nothing,自動 new 一個)
Dim col As New Collection ' ⚠️ 不建議大量重複呼叫 Dim col As Collection ' ✅ Set col = New Collection
4. 早期 vs 晚期綁定
早期(有參考,IntelliSense、較快):Dim dic As Scripting.Dictionary(要參考 Microsoft Scripting Runtime)
晚期(免參考,少 IntelliSense):Dim dic As Object: Set dic = CreateObject("Scripting.Dictionary")
5. With 區塊裡忘了加點
With ws
.Range("A1").Value = "Hi" ' ✅
Range("A2").Value = "Oops" ' ❌ 其實是 ActiveSheet
End With
6. 全域變數污染
能用參數就不用全域;若要全域,用有區分度的命名,集中放一處。
7. 64 位元相容
LongLong / LongPtr 僅供 API/位址用;一般計數仍用 Long。
ByRef vs ByVal(雖是參數,但本質就是變數如何被傳遞)
Sub Foo()
Dim n As Long: n = 10
Inc n ' 預設 ByRef → n 會變 11
IncByVal n ' ByVal → n 還是 11,不會再被改
End Sub
Private Sub Inc(ByRef x As Long)
x = x + 1
End Sub
Private Sub IncByVal(ByVal x As Long)
x = x + 1
End Sub
常見錯誤與排除清單
1. Type mismatch (13):值/物件搞混、Variant 裡頭裝了 Error、或字串轉數值失敗。 → 檢查 IsNumeric、顯式轉型。
2. Object variable not set (91):忘 Set、或物件已變 Nothing。
→ 先 If obj Is Nothing Then。
3. Overflow (6):Integer 溢位、或把大數塞到 Long/Date。
→ 換較大型別、檢查來源。
4. Subscript out of range (9):陣列/集合/工作表索引超界。
→ 先檢查存在性。
5. Application-defined or object-defined error (1004):
多半是 Range/工作表參照錯。
→ 用合格參照 wb.Worksheets("...").Range(...)。
Const / Enum / UDT(讓變數更安全好讀)
Public Const MAX_COL As Long = 26
Public Enum OrderStatus
osPending = 0
osPaid = 1
osShipped = 2
End Enum
' 使用者自訂型別(放標準模組)
Public Type OrderInfo
Id As Long
Amount As Currency
Status As OrderStatus
End Type
Sub UseUDT()
Dim o As OrderInfo
o.Id = 1001
o.Amount = 1999.99
o.Status = osPaid
End Sub
Enum 讓魔法數字消失;Type 把多欄資料綁在一起(Class 需要時再上)。
實戰模板(可直接複製起跑)
Option Explicit
'======== Config / Globals ========
Public Const APP_TITLE As String = "Sales Audit"
Private mRun As Long
'======== Entry ========
Public Sub RunAudit()
Dim wb As Workbook, ws As Worksheet, rng As Range
Dim total As Currency, i As Long
Set wb = ThisWorkbook
Set ws = wb.Worksheets("Sales")
Set rng = ws.Range("B2:B" & ws.Cells(ws.Rows.Count, "B").End(xlUp).Row)
For i = 1 To rng.Rows.Count
total = total + CCur(NzNumber(rng.Cells(i, 1).Value2))
Next
mRun = mRun + 1
MsgBox APP_TITLE & vbCrLf & "Total=" & FormatCurrency(total) & vbCrLf & "#Runs=" & mRun
End Sub
' 將非數字/空值視為 0
Private Function NzNumber(ByVal v As Variant) As Double
If IsError(v) Or IsNull(v) Or IsEmpty(v) Or Trim$(CStr(v)) = vbNullString Then
NzNumber = 0#
ElseIf IsNumeric(v) Then
NzNumber = CDbl(v)
Else
NzNumber = 0#
End If
End Function
速查表
1. Option Explicit:一定要。
2. Long > Integer;金額用 Currency 或 Decimal(Variant+CDec)。
3. 每個變數都寫一次 As 型別。
4. 物件要 Set;外部物件最後 = Nothing。
5. 參數要防副作用→ ByVal。
6. 轉型用 C* 系列函式。
7. Value2 > Value(速度/穩定)。
8. 字串比較受 Option Compare 影響。
9. 需要多欄關聯就用 Enum / Type / Class。
問題集
Q1:真的需要 Set obj = Nothing 嗎?
Excel 內部物件通常不必;但外部 COM(如 Word、Outlook、ADO、FileSystemObject)建議釋放,避免殘留程序或鎖定資源。
Q2:要不要全域變數?
能不用就不用。可改以「函式傳參數」或「回傳結果」;若要全域,集中管理、命名清楚,避免到處修改。
Q3:Variant 很萬用,可以都用它嗎?
不建議。它慢、容易被自動轉型坑到。只在你真的需要多型(如可能回傳數字或錯誤)時用。
Q4:金額用 Double 可以嗎?
會有二進位浮點誤差。用 Currency 或 Decimal(Variant+CDec) 比較穩。
Q5:64 位 Excel 需要改什麼?
一般變數不變;只有 API 宣告/指標要用 LongPtr 或條件編譯(#If VBA7 Then)。
延伸閱讀推薦:
