Enum(列挙型)

にメンテナンス済み

VBA でコードを書く際、列番号や状態を表す数値をハードコードしてしまうことは少なくありません。こうしたマジックナンバーはコードの可読性を大きく下げ、保守を困難にします。そんな問題を解決するのが Enum(列挙型) です。

Enum を使うと、関連する数値定数をグループ化し、意味のある名前で扱えるようになります。さらに、IntelliSense(入力補完)による候補表示も利用できるため、入力ミスの防止にもつながります。

この記事では、Enum の基本的な構文から、列番号管理やフラグ演算といった実践的な活用方法まで詳しく解説します。

マジックナンバーを避ける
プログラムにおいて、マジックナンバーは原則避けるべきコーディングです。VBA 以外の言語においては、少し学びさえすればマジックナンバーが現れることはほぼ無いかと思いますが、VBA ではなかなか避けられないケースは多いです。扱うデータの性質上

Enum が必要になるシチュエーション

実務では、以下のような場面で Enum が威力を発揮します:

  • ワークシートの列番号管理: 各列に意味のある名前を付け、列の挿入・削除に柔軟に対応する
  • 処理の状態管理: 「未処理」「処理中」「完了」「エラー」などのステータスを定数で管理する
  • 関数の引数やオプション: 関数に渡す設定値を、数値ではなく名前で指定する
  • 設定値の一元管理: 色番号、シート番号、カテゴリなど、関連する定数をまとめて管理する

Enum の基本的な構文

Enum の宣言

Enum は、EnumEnd Enum の間にメンバー(要素)を列挙して宣言します。宣言はモジュールの宣言セクション(プロシージャの外側)に記述します。

enum_basic.bas
Enum Fruit
    Apple = 1
    Banana = 2
    Orange = 3
End Enum

Sub ShowFruit()
    Dim f As Fruit
    f = Fruit.Apple

    Debug.Print f  ' => 1
End Sub
宣言セクションに記述する

Enum はプロシージャ (Sub / Function) の内部では宣言できません。必ずモジュールの先頭、プロシージャの外側に記述してください。

値の自動採番

Enum の最大の特徴の一つが、値の自動採番です。最初の要素に値を指定すれば、以降の要素は自動的に 1 ずつ増加した値が割り当てられます。

enum_auto_numbering.bas
Enum TableColumn
    番号 = 1
    名前       ' => 2
    住所       ' => 3
    電話番号   ' => 4
    商品       ' => 5
End Enum

最初の要素の値を省略した場合は、0 から始まります。

enum_zero_start.bas
Enum Color
    Red     ' => 0
    Green   ' => 1
    Blue    ' => 2
End Enum
列番号には 1 始まりを推奨

ワークシートの列番号として Enum を使う場合、Excel の列番号は 1 から始まるため、最初の要素に = 1 を明示的に指定しましょう。

途中から値を指定する

自動採番の途中で値を明示的に指定することも可能です。指定された値以降は、その値を基準に再び 1 ずつ増加します。

enum_skip_numbering.bas
Enum Priority
    Low = 1       ' => 1
    Medium        ' => 2
    High = 10     ' => 10
    Critical      ' => 11
End Enum

Enum のスコープ

Public と Private

Enum には Public(公開)と Private(非公開)の 2 つのスコープを指定できます。

enum_scope.bas
' すべてのモジュールから参照可能
Public Enum Status
    Pending = 0
    Processing = 1
    Completed = 2
End Enum

' このモジュール内でのみ参照可能
Private Enum InternalCode
    CodeA = 100
    CodeB = 200
End Enum
省略時は Public

スコープを省略した場合は Public として扱われます。これは変数 (Dim) や定数 (Const) の省略時の挙動と異なるため注意してください。

定義場所のベストプラクティス

Enum はどこに定義しても 列挙型名.要素名 の形式でアクセスしますが、以下の使い分けが推奨されます:

用途定義場所スコープ
プロジェクト全体で使用標準モジュールPublic
特定モジュール内のみ標準モジュールPrivate
クラスの内部定数クラスモジュールPrivate
Enum はオブジェクトのメンバーにならない

Sheet1 やクラスモジュールに定義した Enum であっても、Sheet1.Status.Completed のようなオブジェクト経由のアクセスはできません。常に Status.Completed のように列挙型名から直接アクセスします。

Enum の使い方

値の参照

Enum の値を参照するには、列挙型名.要素名 の形式を使います。列挙型名を省略して要素名だけでもアクセスできますが、明示的に列挙型名を付ける方が可読性が高くなります。

enum_access.bas
Enum Fruit
    Apple = 1
    Banana = 2
    Orange = 3
End Enum

Sub ShowValues()
    ' 推奨: 列挙型名を付ける
    Debug.Print Fruit.Apple   ' => 1

    ' 省略も可能(ただし非推奨)
    Debug.Print Apple          ' => 1
End Sub
チェック

列挙型名を付けて参照すると、IntelliSense(入力補完)で候補が表示されるため、入力ミスを防ぐことができます。

変数の型として使用

Enum は変数や引数の型として使用できます。型として指定すると、その Enum に定義された値のみを受け取ることが期待されるため、コードの意図が明確になります。

enum_as_type.bas
Enum OrderStatus
    Pending = 0
    Shipped = 1
    Delivered = 2
    Cancelled = 3
End Enum

Sub UpdateOrder(ByVal status As OrderStatus)
    Select Case status
        Case OrderStatus.Pending
            Debug.Print "注文は受付中です"
        Case OrderStatus.Shipped
            Debug.Print "商品は発送済みです"
        Case OrderStatus.Delivered
            Debug.Print "商品はお届け済みです"
        Case OrderStatus.Cancelled
            Debug.Print "注文はキャンセルされました"
    End Select
End Sub

Sub TestOrder()
    ' Enum を引数に渡す
    Call UpdateOrder(OrderStatus.Shipped)
    ' => 商品は発送済みです
End Sub
チェック

Enum 型の引数には、定義された要素以外の数値も代入できてしまいます。VBA の Enum は厳密な型安全性を持たないため、必要に応じて Select CaseCase Else でバリデーションを行いましょう。

関数の戻り値として使用

関数の戻り値にも Enum 型を指定できます。VBA の組み込み関数 MsgBox の戻り値 VbMsgBoxResult がその代表例です。

enum_return_value.bas
Enum ValidationResult
    Valid = 0
    EmptyValue = 1
    InvalidFormat = 2
    OutOfRange = 3
End Enum

Function ValidateAge(ByVal age As String) As ValidationResult
    ' 空チェック
    If Len(age) = 0 Then
        ValidateAge = ValidationResult.EmptyValue
        Exit Function
    End If

    ' 数値チェック
    If Not IsNumeric(age) Then
        ValidateAge = ValidationResult.InvalidFormat
        Exit Function
    End If

    ' 範囲チェック
    If CLng(age) < 0 Or CLng(age) > 150 Then
        ValidateAge = ValidationResult.OutOfRange
        Exit Function
    End If

    ValidateAge = ValidationResult.Valid
End Function

Sub TestValidation()
    Dim result As ValidationResult
    result = ValidateAge("abc")

    Select Case result
        Case ValidationResult.Valid
            Debug.Print "有効な値です"
        Case ValidationResult.EmptyValue
            Debug.Print "値が入力されていません"
        Case ValidationResult.InvalidFormat
            Debug.Print "数値を入力してください"
        Case ValidationResult.OutOfRange
            Debug.Print "0〜150 の範囲で入力してください"
    End Select
    ' => 数値を入力してください
End Sub

実践的な活用例

列番号の管理

Enum の最も代表的な活用例が、ワークシートの列番号管理です。Enum の自動採番機能と列番号の連番は非常に相性が良く、列の挿入・削除にも柔軟に対応できます。

以下のようなワークシートを操作する場合を考えます:

社員番号氏名部署入社日給与
E001田中太郎開発部2020/04/01350000
E002佐藤花子営業部2019/08/15380000
enum_column_management.bas
Enum Col社員マスタ
    社員番号 = 1
    氏名
    部署
    入社日
    給与
End Enum

Sub ProcessEmployeeData()
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("社員マスタ")

    Dim lastRow As Long
    lastRow = ws.Cells(ws.Rows.Count, Col社員マスタ.社員番号).End(xlUp).Row

    Dim i As Long
    For i = 2 To lastRow
        ' 列番号にマジックナンバーを使わない
        Dim empId As String
        empId = ws.Cells(i, Col社員マスタ.社員番号).Value

        Dim empName As String
        empName = ws.Cells(i, Col社員マスタ.氏名).Value

        Dim salary As Long
        salary = ws.Cells(i, Col社員マスタ.給与).Value

        Debug.Print empId & " : " & empName & " : " & Format(salary, "#,##0") & "円"
    Next i
End Sub
列が挿入されたら

「部署」と「入社日」の間に「役職」列が追加された場合、Enum に 役職 を 1 行追加するだけで対応できます。以降の要素は自動的に再採番されるため、コード内の修正は不要です。

複数シートの列管理

複数のシートを操作する場合は、シートごとに Enum を定義すると管理が容易になります。

enum_multi_sheet.bas
Enum Col商品マスタ
    商品コード = 1
    商品名
    カテゴリ
    単価
    在庫数
End Enum

Enum Col受注データ
    受注番号 = 1
    受注日
    顧客名
    商品コード
    数量
    金額
End Enum

Sub CreateOrderSummary()
    Dim wsProduct As Worksheet
    Set wsProduct = ThisWorkbook.Sheets("商品マスタ")

    Dim wsOrder As Worksheet
    Set wsOrder = ThisWorkbook.Sheets("受注データ")

    ' 受注データからデータを取得
    Dim orderNo As String
    orderNo = wsOrder.Cells(2, Col受注データ.受注番号).Value

    Dim productCode As String
    productCode = wsOrder.Cells(2, Col受注データ.商品コード).Value

    ' 商品名は商品マスタから参照
    Debug.Print "受注番号: " & orderNo & " / 商品コード: " & productCode
End Sub

ビット演算によるフラグ管理

Enum の値を 2 のべき乗(1, 2, 4, 8…)で定義すると、ビット演算を使って複数の状態を 1 つの変数で管理できます。

enum_bitflag.bas
Enum Permission
    None = 0       ' 権限なし
    Read = 1       ' 読み取り     (0001)
    Write = 2      ' 書き込み     (0010)
    Execute = 4    ' 実行         (0100)
    Admin = 8      ' 管理者       (1000)
End Enum

Sub CheckPermission()
    ' Or 演算子で複数の権限を組み合わせる
    Dim userPerm As Permission
    userPerm = Permission.Read Or Permission.Write  ' => 3 (0011)

    ' And 演算子で権限を持っているか判定する
    If (userPerm And Permission.Read) = Permission.Read Then
        Debug.Print "読み取り権限: あり"
    End If

    If (userPerm And Permission.Execute) = Permission.Execute Then
        Debug.Print "実行権限: あり"
    Else
        Debug.Print "実行権限: なし"
    End If
    ' => 読み取り権限: あり
    ' => 実行権限: なし
End Sub
ビットフラグとは

2 のべき乗でフラグを定義すると、各フラグが 2 進数の異なるビット位置に対応します。Or 演算で複数のフラグを合成し、And 演算で個別のフラグの有無を判定できます。VBA の組み込み定数 vbYesNoCancel なども同様の仕組みです。

Const との比較

VBA で定数を管理する方法として、Enum のほかに Const ステートメントがあります。それぞれの特徴を比較します。

機能EnumConst
データ型Long(数値のみ)すべての型
グループ化○ 可能× 個別に宣言
IntelliSense○ 候補表示あり× 候補表示なし
自動採番○ 連番が自動割り当て× 手動で指定
型としての利用○ 引数・変数の型に使用可能× 型として使用不可
文字列の定義× 不可○ 可能
小数の定義× 不可(整数のみ)○ 可能
const_vs_enum.bas
' Const で列番号を管理する場合
Private Const COL_EMPLOYEE_ID As Long = 1
Private Const COL_NAME As Long = 2
Private Const COL_DEPARTMENT As Long = 3
Private Const COL_HIRE_DATE As Long = 4
Private Const COL_SALARY As Long = 5

' ↓ Enum を使えばよりシンプルに書ける
Enum ColEmployee
    EmployeeId = 1
    Name
    Department
    HireDate
    Salary
End Enum
使い分けの指針

数値(特に連番) の定数をグループで管理するなら Enum文字列やブール値、小数 の定数を定義するなら Const を使いましょう。

注意点

Enum は整数型(Long)のみ

Enum で定義できる値は Long 型の整数のみです。文字列や小数は使用できません。

enum_only_long.bas
' これはエラーになる
Enum InvalidEnum
    Name = "田中"     ' エラー: 文字列は使用不可
    Rate = 3.14       ' エラー: 小数は使用不可
End Enum

文字列の定数をグループ化したい場合は、標準モジュールに Public Const を並べて擬似的に Enum のように管理する方法があります。

要素名の重複に注意

異なる Enum であっても、Public スコープで同じ要素名を定義するとコンパイルエラーになります。

enum_name_conflict.bas
' コンパイルエラーになる例
Public Enum Fruit
    Apple = 1
    Orange = 2
End Enum

Public Enum Color
    Red = 1
    Orange = 2  ' エラー: Fruit.Orange と名前が衝突する
End Enum
命名の工夫

要素名の衝突を避けるために、プレフィックスを付ける(例: clrOrangefrtOrange)か、片方を Private にするなどの工夫が必要です。

練習問題

Enum で最初の要素の値を省略した場合、その要素に割り当てられる値はどれですか?

正解は「0」です。Enum で最初の要素の値を省略した場合、0 が割り当てられます。2 番目以降の要素は、前の要素の値に 1 を加えた値が自動的に割り当てられます。

次の Enum の C の値として正しいものはどれですか?

Enum Sample
    A = 5
    B
    C
End Enum
正解は「7」です。A = 5 と指定されているため、B は 6、C は 7 が自動的に割り当てられます。
Enum のスコープを省略した場合のデフォルトはどれですか?

正解は「Public」です。Enum のスコープを省略すると Public として扱われます。これは Dim(変数)や Const(定数)の省略時の挙動とは異なる点に注意が必要です。

まとめ

  • Enum は、関連する数値定数をグループ化して管理するための VBA の機能
  • 値の自動採番機能があり、連番の定数を簡潔に定義できる
  • ワークシートの列番号管理に特に有効で、列の挿入・削除にも柔軟に対応できる
  • IntelliSense による入力補完が利用でき、入力ミスを防止できる
  • 変数や引数の型として使用でき、コードの意図を明確にできる
  • Const が文字列や小数に対応できるのに対し、Enum は Long 型の整数のみ
  • スコープ省略時は Public になる点、要素名の重複に注意が必要
マジックナンバーを避ける
プログラムにおいて、マジックナンバーは原則避けるべきコーディングです。VBA 以外の言語においては、少し学びさえすればマジックナンバーが現れることはほぼ無いかと思いますが、VBA ではなかなか避けられないケースは多いです。扱うデータの性質上
変数宣言とデータ型
VBAの変数宣言(Dim)とデータ型(Integer, Long, String, Boolean等)の使い方を初心者向けに徹底解説。Option Explicitによる宣言の強制、変数のスコープ(Public/Private/Dim)、型
条件分岐 (If / Select Case)
VBAの条件分岐(If文、ElseIf、Select Case)の書き方を初心者向けに徹底解説。比較演算子や論理演算子の使い方、ネスト、If文とSelect Caseの使い分けの基準まで、実践的なコード例とともに紹介します。
#VBA #Enum #列挙型 #定数 #コード品質