VBAコーディングルール[随時更新]

# はじめに
私は非IT企業で業務効率化の為2023年10月頃より独学でプログラミングを始めた者です。
2025年4月時点では主にExcelVBAでフォームアプリを作成しています。
以下目的の為私がVBA開発時に意識している内容をまとめ、投稿します。

## 目的
### 1,ルール内容と理由の言語化と記録
普段ぼんやりと意識している内容を言語化することで思考を整理し、そのルールが適切か確認する。
### 2,共同開発者ができた際の情報共有と引継ぎ用
非IT企業かつ個人開発の為、明確なコーディングルールが存在していない。
今後、社内で開発者が増えた際に共同開発できるようルールをまとめる。
将来的には共同開発者とルールの改定等を行って正式なコーディングルールにしたい。そのさきがけ。
### 3,有識者からの意見や知識、レビュー
個人で開発を行っている為、有識者からのアドバイスやレビューの機会がほとんどありません。
間違った知識や考え方,勘違いを修正する機会を少しでも増やすた為、Qiitaに投稿します。
アドバイス等あればコメントお願いします。

# コーディングルール
## ソースファイル全体
### マジックナンバーを使用しない
コードには処理内容のみを記載する事。
基本外部(ワークシートや構成ファイル)から取得し最低でもグローバル定数にしてまとめる。
また、行番号等は適切に列挙型(Enum)を使用する。
### 同じ処理を複数書かない
値が異なるだけの処理を複数書かない、処理を関数化し値を引数で渡す。
### 原則、規定のプロパティ使用による省略をしない
何が省略されているのか分かりにくい。
既定のプロパティの認識間違いにより、正しく動作しない場合がある。
### インデントサイズは４文字を使用し、適切に字下げを行うこと
初期設定(推奨設定)が4をそのまま使用。(逆に2とかだと可読性が下がる為、変更しないこと)
以下の場合は原則字下げを行うこと

If~End If
For~Next
Do~Loop
With~End With
### WorkBook,WorkSheetは必ず指定する
省略した場合ActivebookやActivesheetで処理を行うが、想定外の動作になる可能性がある。
### WorkBook,WorkSheetは可能な限りオブジェクト名で指定する。
シートの順番やシート名はユーザーが変更できるため為、基本オブジェクト名で指定する。
ThisWorkbook以外で参照先が決まっている場合はオブジェクト名をWorksheet.nameで検索しsetする

## ネーミングルール
### 極力日本語を使用しない(推奨)
変数名やオブジェクト名等、極力英語を使用する。(コメントを除く)
日本語の使用にはメリットデメリットあるが、既存のコードは変数名等が英語で書かれていることが多く、コードを読むためにも簡単な英語くらい覚えて読めるようにする為、英語を推奨とする
### 意味が分かるようにネーミングを行い、原則一文字のネーミングを禁止する
一目で変数の内容や処理の内容が分かるようにネーミングを行うこと。
ただし、繰り返し処理で使用するi,j,k等は除く。
例
```vb
Public Const cnsFormName As String ="ふぉ～む"'(フォームの名前を入れる定数)
Dim isJapanOnly As Boolean'(日本のみの場合Trueになるboolean変数)
fncPicFileFullName'(選択したファイルのフルネームを返す関数)
```
### 定数はcns_から始め、変数はそのまま記入する
一目で定数と変数が分かるように。
### 自作関数はfncから始める
一目で自作関数とわかるように。
### 変数名は型のイメージがしやすいネーミングを行う
例
```vb
Dim CreateDate As Date'日付型はDateをいれる
Dim PicFileCnt As Long'整数型はCntやNumをいれる
```

### 配列はネーミングからわかるようにする
一次配列はAry二次配列はArryで終わる
例
```vb
Dim userNameAry As String'ユーザーネームを入れる配列
Dim partsMasterArry As Variant'部品マスタを入れる二次配列
```

### 変数名と型が一致しない場合、変数名の頭に型を記載する
原則、型名含め内容が分かるようネーミングを行うが、どうしても内容と型が一致しない場合、以下のようにネーミングを行う
```vb
Dim strItemNum As String'英数交じりのアイテムナンバーを格納する変数名
```
変数名としてはそのままItemNumを使用したいがNumから通常整数型が使用されていると考えられるため、str_から初めて文字列型を使用していることを強調する。

## 変数
### 変数の宣言を強制する
変数名の記述ミスを避けるため
Option Explicitを必ず記載,ツール→オプション→編集にて設定可能
### 変数の型は明示する
VBAは型の宣言を省略するとVariant型になるが、Variant型を使用する場合も宣言を行い省略を禁止する。
### 宣言は基本1行ずつ行い、可能な限り直前で行う
可読性
### 原則グローバル変数を使用しない
どこからでも変更可能の為、バグが発生しやすい、
基本、毎回計算するか引数等で渡して処理する
### 整数型はLong型を使用しInteger型を使用しない
Integer型とLong型は両方とも整数を保持する変数でサイズに違いがあるが、最新バージョンのVBAではInteger型はLong型に変換される為、パフォーマンス上の利点がなくなった。また、Integer型はLong型に変換する為Long型より動作が遅くなる可能性がある。

参考

https://learn.microsoft.com/en-us/previous-versions/office/developer/office-xp/aa164754(v=office.10)?redirectedfrom=MSDN

### 小数点以下の数値を取り扱う場合は基本Curency型を使用する
Double型等の浮動小数点数型は、演算誤差が出ます。(0.1+0.2=0.3がFalseになる)
小数点以下4桁までの数値を取り扱う場合はCurency型を使用し、小数点以下5桁の数値を扱う場合は有効桁数で丸めて整数化するか、CDec関数でDecimal(Variant型)を使用します。


## 関数
### プロシージャの呼び出しは Call を使用する
callは省略できるが引数の参照方法が変わる場合がある。
また、一目でプロシージャだとわかるように
### 参照渡し(ByRef),値渡し(ByVal)を明示し、基本値渡しを使用する。
既定のプロパティを使用しない。
また、関数の呼び出し方によって参照渡し(ByRef),値渡し(ByVal)が変わるを理解する。
(大きいサイズの値を渡す場合、コピーで消費するリソースが大きい為、参照渡しが望ましい場合もある)

## 演算
### 文字列の結合には&演算子を使用する(＋演算子を使用しない)
&と+は文字列の結合のみなら同じ動作をするが、文字列と数値を結合させる場合エラーになる。
＆は文字列としての結合。＋は数値の計算。

```vb
Dim str_A As String
strA = "AAA"
Dim str_B As String
strB = "BBB"
Dim lng_1 As Long
lng1 = 1
Dim lng_2 As Long
lng2 = 2

Debug.Print strA & strB'AAABBB
Debug.Print strA + strB'AAABBB

Debug.Print lng1 & lng2'12
Debug.Print lng1 + lng2'3

Debug.Print strA & lng1'AAA1
Debug.Print strA + lng1'エラー
```

## 処理
### 複数条件で計算量の多いIf文は処理を分ける
以下のようなif文の場合、fnc_IsTestA()がFalseの場合でもfnc_IsTestB()の処理を行います。
```vb
If fnc_IsTestA(test) = True And fnc_IsTestB(test) = True Then
    '処理内容
Else
End If
```
fnc_IsTestB()の計算量の多く処理に時間がかかる場合は以下のようにします。

```vb
If fncIsTestA(test) = True Then
    If fncIsTestB(test) = True Then
        '処理内容
    Else
    End If
Else
End If
```
### セルへの値の読み書き回数を極力減らす
VBAはセルからの値取得や書き込みに処理時間がかかります。
計算量にもよるが基本以下の処理が望ましい。

セル内容の読み書きはrangeで二次配列にいれて操作を行い一括で張り付ける

```vb:処理時間が遅い
With wsOld
    Dim lastRowOld As Long
    Dim lastColOld As Long
    lastRowOld = .Cells(Rows.count, 1).End(xlUp).row
    lastColOld = .Cells(1, Columns.count).End(xlToLeft).Column
    
    Dim i As Long
    Dim j As Long
    Dim tmp As String
    For i = 1 To lastRowOld
        For j = 1 To lastColOld
            tmp = ""
            tmp = .Cells(i, j).Value
            'データ処理等
            wsNew.Cells(i, j).Value = tmp
        Next j
    Next i
End With 'wsOld
```

```vb:処理時間が早い
With wsOld
    Dim lastRowOld As Long
    Dim lastColOld As Long
    lastRowOld = .Cells(Rows.count, 1).End(xlUp).row
    lastColOld = .Cells(1, Columns.count).End(xlToLeft).Column
    Dim arryOld() As Variant
    arryOld = Range(.Cells(1, 1), .Cells(lastRowOld, lastColOld))
End With'wsOld
'配列上でデータ処理等
wsNew.Cells(1, 1).Resize(UBound(arry_Old, 1), UBound(arry_Old, 2)).Value = arryOld
```
# 参考文献

https://learn.microsoft.com/ja-jp/dotnet/visual-basic/programming-guide/program-structure/coding-conventions

https://qiita.com/mima_ita/items/8b0eec3b5a81f168822d

https://excel-ubara.com/

http://officetanaka.net/index.stm

https://www.tohoho-web.com/ex/vba.html