VBAのマイ コーディング ルール

VBAを使うようになってはや数年。当初は見よう見まねのプログラミングでしたが、段々慣れてくるにしたがってネーミングルール等のコーディングルールが決まってきました。といっても、私は一人プログラマーなのでマイルールです。

これから始める方の参考のために、私のコーディングルールを書いてみます。


Option Explicitを必ず宣言する

変数宣言をしていない場合にエラーを出す機能です。

これを宣言しておかないと、宣言し忘れて大きな問題を引き起こす可能性があります。

変数って宣言しなくちゃいけないの? Office TANAKA

一回設定すれば自動で入力されるので必ず設定しましょう。

Option Explicitって何?


ByValは必ず指定する

プロシージャーで何もつけずに引数を宣言すると、呼び出し元の引数(パラメーター)を呼ばれた側から変更可能な状態になってしまいます。

次の例では、AAAを代入したuParameter1を渡して、uSubで引数uParameter2にBBBを代入すると、uMain内の uParameter1もBBBに書き換わってしまいます。

Sub uMain
    Dim uParameter1 as String
    uParameter1 = "AAA"
    uSub uParameter1
End Sub

Sub uSub(uParameter2)
    uParameter2 = "BBB"
End Sub

これはサブプロシージャーから値を返してもらうことができて、便利な面もあります。しかし、意図せずサブプロシージャーで親プロシージャーの変数を書き換えてしまう恐れもあり危険です。

これを防ぐために、次のように引数には必ず ByVal 修飾子を付けた方が良いでしょう。

Sub uSub(ByVal uParameter2)

詳しくは次のページに書かれています。

引数の値渡しと参照渡し (Visual Basic)

なんで、デフォルトが参照渡し?と思うけれど、BASICだった頃からの名残なのでしょう。


なお、オブジェクトを渡すときにも指定しましょう。そうしないとサブを呼ぶ前はA1セルを参照していたのに、サブを呼んで戻ったらA3セルの参照になってましたとなりかねません。


データ型宣言は必ず行う

データ型の宣言は必ず行います。それもできるだけ正確に行い、ObjectやVariantは極力避けます。(Variantでないと戻り値を受け取れない場合もあります)

データ型を指定するメリットは次の通りです。

  1. インテリセンスが効く
  2. バグを未然に防げるかもしれない
  3. 他の人がわかりやすい

一番のメリットはインテリセンスが効くことです。例えば urow. まで打てば、次のようにインテリセンスが候補を示してくれます。(ショートカットは Ctrl + スペース)

そこから選べば素早く入力できますし、タイプミスをすることはないため作業効率が高いです。

また、意図したプロパティ等が表示されない場合には何かがおかしいと気づくことができます。例えば、変数のデータ型が間違っているときや、編集中のプロシージャー名が重複している場合などです。


2. は、誤ったデータ型の変数を代入しようとすると、エラーを出してくれるので、コーディング中に誤りに気付くことができます。型宣言していないと勝手に型が変換され、とりあえず動いているけれど、ある時動かなくなる、という事もあるでしょう。


3. については、データ型が明確に書かれているので、後から見た人が容易にその変数が何なのかを理解できるという事です。これが Dim objA などと書かれているだけだと、何なのこれ?ってところから始める必要があります。宣言しておいてくれれば一発なのに。

そもそもの話ですが、データ型が指定できない、つまりわかっていないようだと、得体のしれない、たまたま動いているようなコードを書いてしまう恐れもあります。

VBAは自動的な型変換(暗黙的なキャスト)が行われてしまうのでなおさらです。

自分が何をしているのか明確に理解するためにもデータ型を指定した方が良いでしょう。


Privateを指定する

プロシージャーに何もつけないで定義すると、Publicとみなされます。

これだと、他のモジュールから呼び出すことができてしまいます。

例えば、Module1 と Module2 に uSub というプロシージャーを定義し、Module1のuMainから呼び出したとします。通常呼び出されるのはModule1のuSubです。その後、間違えてModule1 のuSubをuSubxに変えたとします。この状態でもuMainはModule2のuSubを呼び出して動作してしまいます。

もしModule2のuSubがModule1と似たようなプロシージャーで、特定の条件の時だけ違う動作をするなら大問題が起こりかねません。また、当面は内容が同じだったとしても、あとからModule2のuSubを変更したとすると、それでも問題が起こります。

明確にPrivateをつけておけば、他のモジュールからは呼び出しできないので、即エラーとなって気が付くことができます。

ちなみに、Module2と3に同じ名前のプロシージャーを作って、Module1から呼び出すと「名前が適切ではありません」と言われてエラーになります。この場合もえっ!?となって調査に時間がかかります。初心者の場合は途方に暮れる可能性があります。


変数名の頭にuなどをつける

最初は型名をつけていました

始めた頃は、よくあるサンプルコードにならって、変数名に objRowとかintCountとか型名をつけていました。しかし、データ型は宣言部分を見ればわかるので、いちいち書く意味がないのでは?と思いました。

また、途中で変数のデータ型を変更した際に変数名を直し忘れると、後から変数名を信じてコードを書いてしまうためバグの元です。変数名の型名部分を直す際に余計な変数(別のプロシージャーの変数)まで間違って直してしまうとそれもまたバグの元です。

上に書いたようにデータ型は必ず宣言した方が良いので、そうなると変数名に型名を付けるのは意味がなくバグの元になるだけです。


変数名やプロシージャー名にuをつける

そこで試してみたのが変数名やプロシージャー名の頭にuを付けるというものです。例えば次のようにです。単純に user 変数の u です。

uList
uRow
uRange
uIndex
uGetDeadLine
uSaveAs

これには次のメリットがあります。

  1. ユーザー定義と一目でわかります。
  2. 短いためコードがすっきりします。
  3. 予約語との衝突を回避できます。

1は、uをつけることにより、システムの変数や関数等との区別がつきます。特に初心者は、何が予約語なのかわからないでしょうから。

2は、変数名が短ければ、それだけコードの見通しも良くなり、行の折り返しも少なくて済むようになります。データ型部分のタイプミスもしづらくなるでしょう。

3は、バージョンアップ等により予約語が追加された際の衝突をさける効果を期待したものです。例えば、GetConfというプロシージャー名を使っていて、後からこれがExcelに実装されてしまうとたちまち動かなくなります。そういった事態を多少は緩和してくれます。


モジュールやフォームはmやfでもよいかも

モジュール名やフォームの頭には mImportWS とか、fProgressBar とかの名前を付けても良いのかもと思っています。

こうしておけば、プロシージャー名との衝突を回避できるし、なんの機能を呼び出しているのか一目でわかります。

例えば、プログレスバーを表示するフォーム内のプロシージャーを呼び出す場合には、 fProgressBar.uInit となり、フォーム内の機能を呼び出しているんだな、とわかります。


字下げを行う

If と End If や With と End With の間の行は一段下げることにより、範囲を明確にします。例えば次のように書きます。

Private Sub uSubIf()
    If a = 2 Then
        If b = 5 Then
            c = 5
        Else
            c = 10
        End If
    Else
    
    End If
End Sub

字下げをしないと、こうなり、どれがどれと対応するのか容易にはわかりません。

Private Sub uSubIf()
If a = 2 Then
If b = 5 Then
c = 5
Else
c = 10
End If
Else

End If
End Sub

With の場合も同様に次のようにします。ここでは、 ListObject も ListRow オブジェクトも .range プロパティを持っているため、範囲を一目でわかるようにしておかないと、誤って指定する恐れがあります。(下のコードは動作しません。説明用です)

Private Sub uSub()
    Dim uList As ListObject
    Dim uRow As ListRow
    
    With uList
        Set uRow = .ListRows.Add
        .Range
        With uRow
            .Range
        End With
    End With
End Sub

この例だと、間違えないよ、と思うかもしれませんが、If文などと組み合わせていると、普段は問題なく動くけれど、特定条件の時に初めてエラーになるという事が考えられます。これだと簡単なテストでは気が付くことができません。

このように、字下げは If や With の有効範囲を一目瞭然にする事ができ、バグを軽減することができます。


コードを書く際には、 if の最後まで書いたら、2回改行して、先に End If を書いてしまいます。それから、上カーソルキーを押して、End If の上に戻り、If条件のコードを書きます。

Public Sub uMain()
    If a = 3 Then
    
    End If
    
    With a
    
    End With
End Sub

With の場合も同様です。あとから書こうなんて思っていると書き忘れます。それに、単純に、後からだと一段下げてEnd If を入力するのが面倒になります。

For Next や Select Case なども同様です。必ず終わりを先に打ってしまいます。


サブプロシージャーに分ける

職場で使っている業務アプリのオプションプログラムがVBAで作られていたため、コードを見てみたところ、字下げがいい加減で、しかも各プロシージャーが異様に長くて(数百行、下手すると千に達していたかもしれません)、ほぼ一つのプロシージャーだけで構成されていて、素人か!?、と思いました。

一つのプロシージャーが長くなると、単純に見通しが悪くなるし、変数も増える傾向にあるため、間違って変数を使ってしまいがちです。例えば、count1 と count2 を使いながら、両者を取り違えるとか。

If 文等を使っていて、その間が長いと、範囲を間違えることもありがちです。End If の対応を間違えて変更するとか。

そのため、プロシージャーが長くなってきたら、機能ごとに分割してサブプロシージャーに分けるのが基本です。


例えば、次の二つのプロシージャを一つにまとめていたら、わけわからんと思います。さらに、uConvertToBMPで呼び出している uChangeColorModeToBW や uSaveAsBMP も含めたら見るのも嫌になります。

'画像ファイルをモノクロ化しBMP形式で保存する
Private Sub uConvertToBMP( _
    ByVal uFilePath As String, _
    ByVal uRecSize As Double, _
    ByVal uMargin As Double)
    
    Dim uPS As Photoshop.Application
    Dim uDoc As Photoshop.Document
    Dim uAnswer As VbMsgBoxResult
    
    Const uDPI As Double = 400

    Set uPS = New Photoshop.Application 'PS 起動
    Set uDoc = uPS.Open(uFilePath)
    
    uAnswer = MsgBox("画像をトリミングしてから OK してください", vbOKCancel)
    If uAnswer = vbCancel Then
        uDoc.Close psDoNotSaveChanges
        Exit Sub
    End If
    uResize uDoc, uRecSize, uMargin, uDPI
    uChangeColorModeToBW uDoc
    uSaveAsBMP uDoc, uFilePath, uRecSize
    uDoc.Close psDoNotSaveChanges
End Sub

'画像サイズを調整する
Private Sub uResize( _
    ByVal uDoc As Photoshop.Document, _
    ByVal uRecSize As Double, _
    ByVal uMargin As Double, _
    ByVal uDPI As Double)
    
    Dim uCanvasSize As Double
    Dim uResize As Double
    Dim uStartUnit As Double
    Dim uPS As Photoshop.Application
    
    uCanvasSize = uRecSize - 0.1 '1mm 小さく
    uResize = uCanvasSize - uMargin / 10 'uMargin は mm
    
    With uDoc
        Set uPS = uDoc.Application
        uStartUnit = uPS.Preferences.RulerUnits
        uPS.Preferences.RulerUnits = psCM
    
        If .Width > .Height Then
            .ResizeImage Width:=uResize, _
                Resolution:=uDPI, ResampleMethod:=psBicubic
            
            .ResizeCanvas Width:=uCanvasSize, _
                Height:=.Height + uMargin / 10, _
                Anchor:=psMiddleCenter
        Else
            .ResizeImage Height:=uResize, _
                Resolution:=uDPI, ResampleMethod:=psBicubic
                
            .ResizeCanvas Width:=.Width + uMargin / 10, _
                Height:=uCanvasSize, _
                Anchor:=psMiddleCenter
        End If
        
        uPS.Preferences.RulerUnits = uStartUnit
    End With
End Sub


コメントは最小限にする

私はコメントは最小限にする派です。

コードを修正する際に、コメントを直すのを忘れがちで、次に見直す際に、その直し忘れたコメントを信じて修正し、バグを作りこむことがあるからです。

例えば次のような場合、最初1の誤差拡散を指定して、やっぱりうまくなかったのでとりあえず 2 に直して50%の閾値判定にして動作して、そのままコメントを直し忘れると、間違ったコメントになります。

uMode = 1 '誤差拡散

この場合は、次のように書くとよいでしょう。

Const uGosaKakusan= 1
uMode = uGosaKakusan

直す場合は

Const uGosaKakusan= 1
Const uHalf= 2
uMode = uHalf

(ここで、uHalf を追加せずに uGosaKakusan = 2 とするのはあまりにも手間を省きすぎです)

このように、コメントで説明するのではなく、できるだけコードで説明するようにしています。

また、プロパティやメソッドが不明な場合、ちゃんと理解してから使った方が良いと思っています。他人の作ったコードを変更する場合、よく理解できてからでないと危ないと思います。生兵法は大怪我のもとです。


なお、例えば数学の公式のように、後からコードだけ見ても、意味を理解するのが困難な場合にはコメントが必須です。

昔 CAD のプログラミングをしていましたが、CAD は数学の塊みたいなもので、あれをコメントなしで書かれるとたまらんと思います。

E = m * c ^ 2 みたいな式ならわかると思いますが。


おわり

というわけで、普段のVBAコード作成方法をマイルールとしてまとめてみました。

VBAをはじめてみたけれど、どう書けばいいの?という方の参考になれば幸いです。


コメント

アクセス数の多い投稿

セキュリティ対策ソフトのノートンが詐欺ソフトまがいになってしまってショック

突然滅茶苦茶遅くなったPCがWindows Updateのキャッシュクリアで復活

Teamsで日本語入力すると左上に変換ウィンドウが出る

Amazon Prime Videoで4K UHD映画を検索する方法

NEC Aterm WX5400HP をセットアップ

Excel 2019 クエリが原因で日本語入力の一文字目が勝手に確定する

Microsoft Flight Simulator (2020)のPS4コントローラー設定

次期主力Wi-Fiルーター NEC Aterm WX5400HP

iPhone 12 Proと iPhone 8 Plusのサイズを比較

Excel VBAからODBCを使ってデータを簡単に取得する