和風スパゲティのレシピ

日本語でコーディングするExcelVBA

数値定数をまとめて管理する - 列挙型変数Enum

列挙型変数(Enum)の基本構文の書き方と、主な使い方について解説します。

列挙型変数の宣言方法

' 基本構文
Enum [本体の名前]
    [要素1の名前] = [設定する数値]
    [要素2の名前] = [設定する数値]
        … 好きなだけ増やしてOK
End Enum

' 宣言例
Enum 漢数字
    一 = 1= 2= 3= 4= 5
End Enum

列挙型変数は、数値(Long)の定数をまとめて定義するしくみです。

Const 漢数字の一 = 1
Const 漢数字の二 = 2
Const 漢数字の三 = 3
Const 漢数字の四 = 4
Const 漢数字の五 = 5

このような定数をたくさん定義する代わりに、列挙型を使います。

性質としては完全に「定数」なので、列挙型定数と呼んだ方がわかりやすいですね。

列挙型の使い方

MsgBox (漢数字.+ 漢数字.) ' 8 が表示されます

「本体の名前」と「要素の名前」を「.」でつなぐことで、「=値」とした数値を呼び出すことができます。

できるのは呼び出しだけで、値の代入はできません。
完全に「定数」だと思ってください。


列挙型のとても便利な点は、入力時に選択肢が出ることです。

列挙型の入力選択肢

↑↓で選んでTabで入力することができます。

=部分を省略した時の値

列挙型の「=値」部分は省略でき、その場合は「連番になるように」採番されます。
最初の要素を省略した場合は「0」が割り当てられます。

今回の「漢数字」は、こう書くこともできます。

Enum 漢数字
    零
    一
    二
    三
    四
    五
End Enum


また、↓のように、飛び飛びで値をつけることもできます。

Enum 漢数字
    一 = 1
    二
    三
    五 = 5
    六
    九 = 9End Enum

「=値」があるところはその値を、それ以外はひとつ前から連番を振ってくれます。
↑の例は4, 7, 8 が抜けていますが、漢数字通りに割り当てられます。

列挙型を使える範囲(スコープ)

変数とは違い、プロシージャの内部では宣言できません

また、「Public/Private」を省略した場合はPublicとして扱われます。
変数・定数と違うため注意が必要です。

まとめると、

宣言方法 使える範囲
SubやFunctionの中で宣言 できない
Subの外(モジュールの一番上)で宣言 ブック内すべて
Enumの前にPrivateをつける そのモジュール内のみ

というスコープになります。


この仕様は「省略時にPublic/Prrivateのどちらになるかが、同時に使用することの多い定数と異なる」点が、とても厄介です。


よって、「列挙型はPrivate/Publicを省略しない」というルールで運用することをおすすめします。

「Public Enum / Private Enum」のどちらかで宣言しましょう。

何で列挙型を使うの?

根本的な使用目的は、定数と同じです。

直打ちの数字を無くすことで、読みやすくて、変更に強いコードを書くために使います。

この「定数を使う意味」がまだピンとこない方は、↓の記事をおすすめします。
定数の使い方は、中級者への第1歩といってもいいくらい大事な基礎の内容なので、
習得しておくと今後のVBAライフが捗ると思います。

中に列挙型も出てきますので、移動する場合は本ページはもう読まなくてOKです。

www.limecode.jp

定数の良さはわかっているから、列挙型ならではの良さを知りたい
という方は、このまま読み進めてください。

なにを列挙型にすればいいの?

定数よりも列挙型を使った方が良い場面は、

  • ひとまとめにして扱いたい
  • 連番になることが多い
  • 要素の追加や並び替えが多い

このあたりが該当するときになります。

そして、これらが完璧に活かせるのが「列番号」です。


ワークシートで表形式のデータを扱うVBAにおいて、

Cells(R, 4) = Cells(R, 2) * Cells(R, 3)
や
Range("D" & i) = Range("B" & i) * Range("C" & i)

このように書かれているマクロは、
列挙型を使って

Enum 売上データの列
    購入日 = 1
    単価
    個数
    金額
End Enum

Cells(R, 売上データの列.金額) = Cells(R, 売上データの列.単価) * Cells(R, 売上データの列.個数)

こう書くことができます。


まずは読みやすさの恩恵を最大限に得ているのがわかりますね。

定数なしのサンプルは何のことかわからないので、
コメントや実際のシートを見に行かなければいけません。


対して列挙型は、「金額=単価×個数」と、読めばわかるコードになりました。


まあここまでは「定数の良さ」と同じメリットなのですが、
列挙型の真価を発揮するのは、列番号と連番機能の相性の良さです。
 

Enum 売上データの列
    購入日 = 1
    単価
    個数
    金額
End Enum

「購入日 = 1」 が「購入日がA列である」ことを意味しますが、
B列以降の見出し名をたてに並べるだけで、
「単価=2(B列)」「個数=3(C列)」…と自動採番されます


この機能のおかげで、初回の入力が楽になるだけでなく、
「列の挿入や並び替え」にも、とても簡単に対応できます。


例えばB列に「購入者」の列を挿入したとします。

B列挿入に対応するための列挙型の変更は、なんと列の名前を足すだけです。


「購入日」と「単価」の間に「購入者」を入れれば、「購入者」が2になって、それ以降は3,4,…に再採番されます。

Enum 売上データの列
    購入日 = 1
    購入者    ' ← これを書き入れた
    単価       ' ↓ここから下は数値が1ずつ増え、C列以降にズレたことと連動してくれる
    個数
    金額
End Enum

素晴らしいですね。

Cells(i, 23) = Cells(i, 32) * Cells(i, 34)

過去に↑のマクロの「列の挿入」を命じられ、地獄を見た経験のある私にとっては、
神と崇めたい機能です。


このように列挙型変数は、「列番号を定義するために作ったんじゃないか?」ってくらいの仕様なので、表形式のシートは、列番号を必ず列挙型で定義しておく癖をつけましょう。

「シート名.列見出し名」を命名規則にすれば、命名に悩む必要もないですし、
「列見出しをコピーして、行列を入れ替えて貼り付け」してからVBAにコピーすれば、
列挙型の宣言部分をExcelが作ってくれます。

積極的に活用していきましょう。

おまけ

「○○の列」というネーミングについて

列挙型を扱っていると、その名前をどう名付けるか考えるようになります。

↑で使っていた「○○の列」はさすがにちょっと冗長で見づらいです。


ひとつの例として、私は「アプリケーションハンガリアン」を採用しています。

変数名の頭に、シートなら「ws」、セルなら「cell」などをつけて、
「ws集計表」「cell更新セル」などと名付けるルールで、今回の列番号には「CNo」を接頭しています。

' ワークシートの変数・定数名
Dim ws集計シート As Worksheet
Dim wsデータシート As Worksheet

' 列番号の変数・定数名
Enum CNo購入歴データ
    購入日 = 1
    購入者
    単価
    個数
    金額
End Enum


詳しい解説は専用の記事に任せるとして、目的はこれです。

アプリケーションハンガリアン日本語変数の入力アニメ

シートが欲しいときにシートの、セルアドレスが欲しいときにセルアドレスの入力候補をくれるうえに、
接頭が英語だと日本語入力をOFFのままで日本語変数が入力できます

↑の例は、これだけ日本語変数を使っていますが、すべて半角入力のまま入力しています。

加えて列番号を「CNo」にすれば、「Cells(R, C)」っぽく見えるようになるので、「○○の列」よりも見やすくなります。

Cells(R, CNo売上データ.金額) = Cells(R, CNo売上データ.単価) * Cells(R, CNo売上データ.個数)

興味があれば、ぜひ↓の記事を読んでみてください。

www.limecode.jp

ちょっと上級者な使い方

この列挙型変数ですが、実はこんな使い方ができます。

列挙型の特殊な使い方

[As 漢数字] に注目してください。

列挙型をデータの型にした変数を宣言でき、選択肢から値を代入することができるようになります。


ただし、残念ながら代入される値はコントロールできないようで、

Enum 漢数字
    一 = 1= 2= 3= 4= 5
End Enum


Dim x As 漢数字

x = 10 ' ← 列挙型で定義されていない数値だが、エラーにならない。

MsgBox (x) ' ← 10がちゃんと出る。やっぱりエラーにならない。


' なんでだろう?と型を調べてみる

MsgBox (VarType(x)) ' ← 3がでる。すなわちLong型。

↑のように、xの正体はLongさんのようです。惜しい。


同じLongでも、渡す値が決まっていたり、値ごとに意味を持っている場合は、
「 As Long 」ではなく、「 As 列挙型名」で宣言することができます。

想定外の値の除外には使えませんが、入力が楽になり、コードに意味を持たせることができます。


あまり頻繁に使う技法ではありませんが、心の片隅にでも置いておいてください。