VBA|列挙体(Enum)を使うと表の見出しの増減に強いマクロができる

列挙体(Enum)をつかったマクロの解説記事のアイキャッチ画像

VBAの列挙体(Enum)とは

列挙体はかんたんに言えば複数の定数をまとめて宣言ができるものです。定数とは、変えられない値を代入するものであり、VBAではConstをつかって宣言し、宣言と同時に値を決めておくものです。

列挙体を利用するためには、まず宣言セクションでEnumステートメントをつかって宣言をします。ステートメントは異なるものの、別の記事で紹介した「構造体」と同じ方法なのでまとめておぼえるといいでしょう。

ユーザー定義型(構造体)の記事については、以下のリンクよりご覧いただけます。

宣言した列挙体は、宣言時に指定した範囲(スコープ)で利用できます。

そもそも定数なら、利用する場面がそんなに多くないのではないか。といった疑問をもたれそうですが、使い方の工夫しだいで便利なものとなるので、次の章では列挙体(Enum)をつかうメリットを紹介していきます。

独学でプログラミングをはじめてみたけど、このままの学習方法に不安を感じているのなら、
オンラインスクールで現役エンジニアのサポートがあるテックアカデミーがおすすめ。
スキマ時間に学べて仕事も保証。必ず副業、始められます。まずは無料でプログラミング体験

VBAで列挙体(Enum)をつかうメリット

さて、列挙体(Enum)をつかうメリットについてですが、結論から言えば、プログラムのカスタマイズやアップデートがしやすいマクロが作れるようになります。また、列挙型を宣言するとモジュールでのプロシージャで名前まで入力するとメンバーが自動で表示される機能(自動入力補完機能)が使用できるため、VBAのコーティングが楽になります。

自動入力補完機能(インテリセンス)が作動した画像
列挙体を宣言したことで自動入力補完機能(インテリセンス)が作動した画像
列挙体名を入力してピリオド(.)を入力すると、画像のようにメンバー名が表示され、選択できるようになります。

アップデートやカスタイズがしやすいとはどういうことか。ですが、これはプログラムが読みやすく、編集がしやすいこととなります。例えば、エクセルの表で商品のIDや商品名、仕入価格や販売価格データを以下のようにまとめていたとします。

行/列 A列 B列 C列 D列
1行 商品ID 商品名 仕入価格 販売価格
2行 Sample-001 商品A 100 180
3行 Sample-002 商品B 200 360
4行 Sample-003 商品C 300 540

エクセルのデータは表形式なので縦(行)と横(列)の形式が基本ですが、このデータのうち、商品Aの販売価格の値を取得する場合のVBAのコードであれば、以下のように想定ができます。

商品Aの販売価格を取得するVBAコード

Sheet1.Range(“D2”).value

または、

Sheet1.Cells(2,4) .value

※表はSheet1に書かれているものとします。

しかし、ある日、この表のB列とC列の間に該当商品の「メーカー名」を挿入することになった場合はどうなるでしょうか。以下に列を追加した表を掲載しておきます。

行/列 A列 B列 C列 D列 E列
1行 商品ID 商品名 メーカー名 仕入価格 販売価格
2行 Sample-001 商品A XX株式会社 100 180
3行 Sample-002 商品B 株式会社YY 200 360
4行 Sample-003 商品C 合同会社ZZ 300 540

D列に入力されている「販売価格」はB列とC列の間に追加された「メーカー名」が挿入されることで、E列になってしまい、先に書いたマクロで取得できる値は商品Aの情報は「販売価格」から「仕入価格」になります。

もし、こちらのマクロで請求書を発行する処理を実施していた場合、販売価格ではなく、仕入価格による請求書が発行されてしまって大惨事といったことにもなりかねません。

また、このアップデートに対応するために、値を取得するために指定していた「Sheet1.Range(“D2”).value」や「Sheet1.Cells(2,4) .value」が使われている部分を膨大なコードの中からすべて見つけて「Sheet1.Range(“D2”).value」なら「Sheet1.Range(“E2”).value」に、「Sheet1.Cells(2,4) .value」なら「Sheet1.Cells(2,5) .value」に修正していく作業は、非常に神経をすり減らす作業となってきます。

なお、プログラミングにおいて製作者しかわからないような直に書かれた数値をマジックナンバーと呼びますが、プログラミングでは、このマジックナンバーはなるべく使用しないことが望ましいとされています。これはマジックナンバーがあると、製作者以外には数値の意図や意味が理解できず、VBAのコードが読みにくくなるからであるからです。

また、これはほかの人と協力してシステムを開発するエンジニアだけに限ったわけではなく、1週間、数ヵ月、数年後の自分と、そのマクロを制作した時点の自分との間にも当てはまってくるでしょう。

これらについて列挙体(Enum)をつかうことで解消することができます。
ここで紹介したあるデータが書かれた表に列が挿入された場合であっても、列挙体(Enum)で宣言したものの値を変更するだけでアップデートが完了することができます。

では、どのようにすれば良いかの詳しい内容は、後述のサンプルマクロで解説しますが、まずは列挙体(Enum)のつかいかたについて次の章で紹介します。

独学だと中々スキルが身についた実感が湧かない。学習環境を見直してみませんか?

エクセルで繰り返しや転記作業で苦しい思いをした経験はありませんか?
今まで苦労してきたその作業を簡単なプログラムをおぼえるだけで解決できる可能性があります。
なるべくお金や時間をかけずにエクセルマクロVBAを習得したい人にはこちらの「1日速習講座」がおすすめです。

VBAで列挙体(Enum)のつかいかた

本章では、列挙体のEnumステートメントをつかって宣言します。

列挙体(Enum)の構文

[ Public | Private ] Enum 列挙型名(name)
メンバー名(membername)1 [=値(constantexpression)]
メンバー名(membername)2 [=値(constantexpression)]
End Enum

[]は省略可能な部分です。

構文だと難しくみえるけど、引数や書きかたについて次から見ていくよ。

列挙体(Enum)の引数について

引数については以下のとおりです。

引数の種類 説明 必須Or省略可能 省略時の値
列挙体名(name) 列挙体の名前を指定します。 必須
メンバー名(membername) 列挙体を構成するメンバーの名前を指定します。 必須
値(constantexpression) 数値を指定します。長整数型(Long)の値を代入します。 省略可能 長整数型(Long)の値が代入されます。先頭のメンバーの場合は0が、それ以降のメンバーなら直前のメンバーの値に+1がされた数値が代入されます。
※スコープ(適用範囲)を指定するPublicやPrivateについては以下の記事を参考にしてください。なお、スコープの指定を省略すると既定値のPublicとなります。Privateでは列挙体(Enum)が存在するモジュールのなかでのみ参照が可能です。
ユーザー定義型(構造体)の記事については、以下のリンクよりご覧いただけます。マイクロコピーテキスト

列挙体(Enumステートメント)は宣言セクションでのみ使用が可能です。

プロシージャ内で記入した場合はコンパイルエラーが発生します。また、宣言セクションでメンバーに指定した値をプロシージャで変更しようとした場合も同じくコンパイルエラーが発生します。つまり宣言時に指定した値からは変更できません。

ここまでで、列挙体(Enum)の基本的な使いかたを見てきましたので、続いて列挙体(Enum)を使ったサンプルマクロを見ていきましょう。


PCでスキルアップをしたい・Excelをしっかり学んで社内の評価を高めたい人は必見!
実務をプロから学べる「ユースフル」は講座の動画は永年見放題。安心のQ&A機能で分からないを解決。

VBAで列挙体(Enum)をつかったサンプルマクロ

この章では列挙体(Enum)をつかったサンプルマクロを紹介します。

まずは、シンプルに列挙体(Enum)を宣言して、デバッグプリントでイミディエイトウィンドウに列挙体(Enum)のメンバーに指定した値を出力するマクロです。

Option Explicit

'宣言セクションで列挙体(Enum)を宣言
Private Enum 商品データ見出し

    商品ID = 1
    商品名 = 2
    仕入価格 = 3
    販売価格 = 4
    
End Enum
'プロシージャで列挙体(Enum)のメンバーをデバッグプリントで出力する
Sub Enum_test()
    
    Debug.Print 商品データ見出し.商品ID '実行結果:1
    Debug.Print 商品データ見出し.商品名 '実行結果:2
    Debug.Print 商品データ見出し.仕入価格 '実行結果:3
    Debug.Print 商品データ見出し.販売価格 '実行結果:4
    
End Sub
※実行結果はイミディエイトウィンドウに表示されます。

宣言セクションで列挙体を宣言します。Enumステートメントをつかって、メンバー名にイコールで値を代入します。プロシージャ(Enum_test)ではデバッグプリントでメンバーの値を出力しています。

つづいて、メンバーの値を省略した場合のケースになります。

Option Explicit

'宣言セクションで列挙体(Enum)を宣言
Private Enum 誕生石

    ガーネット          '1月の誕生石
    アメジスト          '2月の誕生石
    アクアマリン = 3    '3月の誕生石
    ダイヤモンド = 4    '4月の誕生石
    エメラルド = 5      '5月の誕生石
    パール = 6          '6月の誕生石
    ルビー              '7月の誕生石
    ペリドット          '8月の誕生石
    サファイア = 90     '9月の誕生石
    オパール            '10月の誕生石
    トパーズ            '11月の誕生石
    トルコ石            '12月の誕生石
    
End Enum
'プロシージャで列挙体(Enum)のメンバーをデバッグプリントで出力する
Sub Enum_test2()
    
    Debug.Print 誕生石.ガーネット   '実行結果:0  値を指定しない最初のメンバーは0
    Debug.Print 誕生石.アメジスト   '実行結果:1  2つ目以降のメンバーは直前のメンバーの値に+1
    Debug.Print 誕生石.ルビー       '実行結果:7  直前のメンバーの値が6なので7
    Debug.Print 誕生石.サファイア   '実行結果:90 値を指定しているので90
    Debug.Print 誕生石.オパール     '実行結果:91 直前のメンバーの値が90なので91
    
End Sub
※実行結果はイミディエイトウィンドウに表示されます。

最初のメンバーの値が省略された場合は0になること、それ以降は直前のメンバーの値に+1がされた値が代入されていることがわかります。なお、メンバーの値はほかのメンバーと同じでもエラーが発生することはありません。

次は、エクセルの表からデータを取得するマクロです。以下の表からデータを取得します。

サンプルマクロで処理をするデータが入力されたエクセルの表
サンプルデータの表A

次のマクロは、ユーザーより入力された数量を受け取って、販売価格の合計、仕入価格の合計と利益を返す処理を実行します。テストでは数量の入力で10を指定しています。

Option Explicit

'宣言セクションで列挙体(Enum)を宣言
Private Enum 商品データ見出し列
'エクセルの表の列数をメンバーに代入
    商品ID = 1
    商品名 = 2
    仕入価格 = 3
    販売価格 = 4
    
End Enum
'プロシージャで列挙体(Enum)のメンバーを利用して処理をする
Sub Enum_test3()
    
    Dim n As Long
    With Worksheets("商品データ")
        n = Application.InputBox("商品の販売数量を入力してください。", "数量の入力", 1, Type:=1) '10を入力
        '販売価格の合計を計算してメッセージボックスで表示する
        MsgBox ("販売価格の合計:" & .Cells(2, 商品データ見出し列.販売価格) * n & "円です")
        
        '仕入価格の合計を計算してメッセージボックスで表示する
        MsgBox ("仕入価格の合計:" & .Cells(2, 商品データ見出し列.仕入価格) * n & "円です")
        
        '利益を計算してメッセージボックスで表示する
        MsgBox ("利益:" & .Cells(2, 商品データ見出し列.販売価格) * n - .Cells(2, 商品データ見出し列.仕入価格) * n & " 円です")
    End With

End Sub

このマクロと同じ処理を、列挙体(Enum)をつかわなかった場合も比較用に掲載しておきます。

Option Explicit

Sub NoEnum_test()
    
    '列挙体(Enum)を使わないで処理をする
    Dim n As Long
    With Worksheets("商品データ")
        n = Application.InputBox("商品の販売数量を入力してください。", "数量の入力", 1, Type:=1) '10を入力
        '販売価格の合計を計算してメッセージボックスで表示する(180円×10個)
        MsgBox ("販売価格の合計:" & .Cells(2, 4) * n & "円です")
        
        '仕入価格の合計を計算してメッセージボックスで表示する(100円×10個)
        MsgBox ("仕入価格の合計:" & .Cells(2, 3) * n & "円です")
        
        '利益を計算してメッセージボックスで表示する(1,800円-1,000円)
        MsgBox ("利益:" & .Cells(2, 4) * n - .Cells(2, 3) * n & " 円です")
    End With

End Sub

列挙体(Enum)をつかわずに列数の値を指定することで実現しています。ここまでであれば、VBAのコード量も短いですし、列挙体(Enum)をつかうメリットは感じられないと思います。しかし、列挙体(Enum)のメリットを紹介する章でもお伝えしたとおり、表に新たな列を追加することになったと考えてみましょう。

変更内容は、元の表データのC列(商品名)とD列(仕入価格)の間に新たに「メーカー名」を挿入したこととします。
実際に追加した表を以下に掲載します。

「メーカー名」の列を追加した結果の画像
サンプルデータの表B

さて、このように表が変更されたことで、サンプルマクロはどのようなアップデートが必要になるか見ていきましょう。さきに列挙体(Enum)をつかって作成したマクロの場合のアップデートは以下のようにします。

Option Explicit

'宣言セクションで列挙体(Enum)を宣言
Private Enum 商品データ見出し列
'エクセルの表の列数をメンバーに代入
    商品ID = 1
    商品名 = 2
    メーカー名 = 3  'メンバーにメーカー名を追加し、列数を調整
    仕入価格 = 4   'メンバー追加に伴い列数を調整
    販売価格 = 5   'メンバー追加に伴い列数を調整
    
End Enum
'プロシージャで列挙体(Enum)のメンバーを利用して処理をする
Sub Enum_test3()
    
    Dim n As Long
    With Worksheets("商品データ")
        n = Application.InputBox("商品の販売数量を入力してください。", "数量の入力", 1, Type:=1) '10を入力
        
        '販売価格の合計を計算してメッセージボックスで表示する(180円×10個)
        MsgBox ("販売価格の合計:" & .Cells(2, 商品データ見出し列.販売価格) * n & "円です") '1,800
        
        '仕入価格の合計を計算してメッセージボックスで表示する(100円×10個)
        MsgBox ("仕入価格の合計:" & .Cells(2, 商品データ見出し列.仕入価格) * n & "円です") '1,000
        
        '利益を計算してメッセージボックスで表示する(1,800円-1,000円)
        MsgBox ("利益:" & .Cells(2, 商品データ見出し列.販売価格) * n - .Cells(2, 商品データ見出し列.仕入価格) * n & " 円です")
    End With

End Sub

表の更新にともなって書きかえた部分は、赤色のマーカーをした部分のみです。プロシージャにいたってはさわることさえしていません。とてもスピーディーで安全にアップデート対応ができました。これこそが列挙体(Enum)で指定した場合の最大のメリットと言えます。

一方で、列挙体(Enum)をつかわずに処理していたマクロのアップデートはどうでしょうか。

Option Explicit

Sub NoEnum_test()
    
    '列挙体(Enum)を使わないで処理をする
    Dim n As Long
    With Worksheets("商品データ")
        n = Application.InputBox("商品の販売数量を入力してください。", "数量の入力", 1, Type:=1) '10を入力
        '販売価格の合計を計算してメッセージボックスで表示する(180円×10個)
        MsgBox ("販売価格の合計:" & .Cells(2, 5) * n & "円です")  '列数を4から5に変更
        
        '仕入価格の合計を計算してメッセージボックスで表示する(100円×10個)
        MsgBox ("仕入価格の合計:" & .Cells(2, 4) * n & "円です")  '列数を3から4に変更
        
        '利益を計算してメッセージボックスで表示する(1,800円-1,000円)
        MsgBox ("利益:" & .Cells(2, 5) * n - .Cells(2, 4) * n & " 円です")  '列数を3→4、4→5に変更
    End With

End Sub

こちらは列挙体(Enum)を使っていないため、プロシージャのコードを編集する必要がでてきます。
赤色のマーカー部分が編集した部分になりますが、列挙体(Enmu)を使ったものと比較すると、影響範囲がコードのあちらこちらに散ってしまうので、アップデートの対応がどこまでなのかがみきわめにくいです。

さらにVBAのコード量が増えれば増えるほど、アップデートのしやすさに明確な違いが出てきますので、列挙体(Enmu)を使う効果が実感できると思います。

列挙体(Enum)を使っていれば、マクロのアップデートがカンタンだね。

つづいてはエラーが発生するサンプルマクロを紹介していきます。

VBAで列挙体(Enum)でエラーが発生するケース

こちらでは、引き続き列挙体(Enum)を使ったサンプルマクロでエラーとなるケースを紹介していきます。列挙体(Enum)を使うマクロの作成時の参考にしてください。

Option Explicit

'宣言セクションで列挙体(Enum)を宣言
Private Enum 組みわけ
    
    グリフィンドール = 1
    ハッフルパフ = 2
    レイブンクロー = 3
    スリザリン = 4
    
End Enum
'プロシージャで列挙体(Enum)のメンバーの値を変更しようとする
Sub Enum_test_error()

    'メンバーであるグリフィンドールの値を1から5に変更しようとしているためエラーが発生する
    組みわけ.グリフィンドール = 5
    
End Sub
Option Explicit

'宣言セクションで列挙体(Enum)を宣言
Private Enum 組みわけ
    'メンバー名が重複しているため実行エラーが発生する
    グリフィンドール = 1
    ハッフルパフ = 2
    レイブンクロー = 3
    グリフィンドール = 10
    
End Enum
'プロシージャで列挙体(Enum)のメンバーをデバッグプリントで出力する
Sub Enum_test_error()
    
    Debug.Print 組みわけ.グリフィンドール
    
End Sub
Option Explicit

'宣言セクションで列挙体(Enum)を宣言
Private Enum 組みわけ

    グリフィンドール = 1
    ハッフルパフ = 2
    レイブンクロー = 3
    スリザリン = 4
    
End Enum
'プロシージャで列挙体(Enum)のメンバーの値を変更しようとする
Sub Enum_test_error()
    
    組みわけ.グリフィンドール = 5
    
End Sub
Option Explicit

'宣言セクションで列挙体(Enum)を宣言
Private Enum 組みわけ
    
    グリフィンドール = 1
    ハッフルパフ = 2
    レイブンクロー = 3
    スリザリン = 4
    
End Enum

'プロシージャで列挙体(Enum)を宣言するとエラーが発生する
Sub Enum_test_error()

    'プロシージャで列挙体(Enum)名「組みわけ2」の宣言文
    Enum 組みわけ2
    
        ゴドリック = 1
        ヘルガ = 2
        ロウェナ = 3
        サラザール = 4
    
    End Enum

    Debug.Print 組みわけ2.サラザール; "・"; 組みわけ.スリザリン

End Sub
'標準モジュール1
Option Explicit

'宣言セクションで列挙体(Enum)を宣言
Enum 組みわけ 'スコープの指定を省略した場合は既定値に従い「Public」となる
    
    グリフィンドール = 1
    ハッフルパフ = 2
    レイブンクロー = 3
    スリザリン = 4
    
End Enum
'標準モジュール2
Option Explicit

'宣言セクションで列挙体(Enum)を宣言しているが、標準モジュール1の列挙体名と重複している
Public Enum 組みわけ
    
    桔梗組 = 1
    菊組
    山茶花組
    椿組
    華組
    牡丹組
    桃組
    楼組
    
End Enum
Sub Enum_test_error()

    Debug.Print 組みわけ.山茶花組
    
End Sub
列挙体(Enum)のスコープの既定値は「Public」であるため、標準モジュール1と標準モジュール2の列挙体名が重複することになる。

どちらかの列挙体のスコープを「Private」に変更するとエラーは出なくなるよ。

VBAで列挙体(Enum)まとめ

この記事では、VBAで列挙体(Enum)と呼ばれるものについて紹介してきました。列挙体(Enum)は複数の定数(変化しない固定の数値)をまとめて宣言し、処理に利用することができるものです。また、列挙体(Enum)を利用するメリットは、VBE上でインテリセンス(自動入力補完機能)の利用ができることと、マクロのアップデートやメンテナンスがしやすくなるといったものでした。

さいごに箇条書きでこちらの記事をまとめておきます。

  • 列挙体(Enum)を使うメリット
    • VBE上でインテリセンス(自動入力補完機能)の利用ができる
    • マクロのアップデートやメンテナンスがしやすくなる
  • 列挙体(Enum)を使いかた
    • 宣言セクションで列挙体名とメンバー名を宣言する
    • プロシージャの処理で列挙体名.メンバー名で値を参照できる(ピリオド)
  • 列挙体(Enum)を使うときの注意点
    • 列挙体(Enum)はプロシージャのなかでは宣言できない
    • 列挙体(Enum)のメンバーの値は数値以外に指定できない
    • 列挙体(Enum)のメンバーの値はプロシージャのなかで更新・変更はできない
    • 同じ列挙体(Enum)のなかでメンバーの重複はできない
    • 適用範囲(スコープ)の既定値は「Public」であるため、同じ適用範囲内で列挙体名の重複はできない

列挙体(Enum)を使わなくてもマクロが作れないわけではないですが、プログラミングスキルでは、いかに読みやすいコードを書いて、修正やアップデートを安全にできるようにそなえておくかも大切です。便利なものを適切な場所で柔軟に利用するチャレンジを続けることで幅広い知識が身につくようになるでしょう。

あなたのマクロをつくるためにこちらの記事が少しでもお役にたてば幸いです。

コダマのもりブログはにほんブログ村に登録しています。
ブログの記事が役に立ったと感じて頂けたら、フォローお願いいたします。

コメント

タイトルとURLをコピーしました