C#ばっかり触ってきた筆者が、VBAを触って発狂している記事です。
型システムがおわっている
「Variant型」という謎の型
Variant型とは、なんとも形容しがたい型があります。
雰囲気としては、C#の「dynamic型」に近いです。
本当に何でも代入できる型で、代入時に型が決まるような挙動をします。
そして別の型を再代入すればその型に代わります。つまり何が入っているか、まったくわからない!
コード補完はきかないし、型チェックも行われない。
これだけなら「へー」で住むのだが、VBAの気持ち悪さをなめてはいけません。
VBAには、Variant型にしか代入できないことが多々あります。
配列でFor Eachを使う場合
Dim array() As String
array(0) = "い"
array(1) = "ろ"
array(2) = "は"
Dim item As Variant
For Each item In array()
MsgBox item
Next
String型の配列でも、ForEachで使うitemはVariant型にしなければならない。
String型で宣言すると落ちます。
「Object型」という謎の型
似たように何でも代入できる型に、object型という型もあります。
こちらもC#のObject型に似てる…けど、Integerなどを代入できないので、どちらかというとJavaっぽい。
ただし、特にキャストしなくてもメンバを参照できるdynamic型のような挙動もします。
違う型を入れて共通のメソッドを呼び出す、ダックタイピング的なことができるのは少し面白いです。
参照型はSetで代入する。
数値型とかのプリミティブ(?)な型はそのまま代入します。
X = 10
参照型はSetをつけないと実行時エラーになります。
Set X = Range("A1:Z10")
参照型と値型分けるのはいいんですけど、こんな型ゆるゆるなのにそこ気にする?という感想です。
ちなみに値型は
Let X = 123
のLetを省略したものらしい。しらんがな
配列がおわっている
上の例で配列を使いましたが、いろいろと構文が気持ち悪いです。
まず、要素のアクセスに()を使うのが個人的に好きではありません
が、言語の書き方の問題。
でも、添え字が0から始まったり1から始まったりするのはやめません?
添え字の「0」始まりと「1」始まりが混在する。
VBAでは、添え字が何から始めるか指定することができます。
以下のように書くと1から始まり
Dim arr(1 To 10) As String
なにもしないと、0から始まり
Dim arr(10) As String
エクセルのRangeなどは1始まります。
Range("A1:Z10")
頭おかしくなっちゃう
配列長がとれない
VBAでは配列の長さがとれません。
一応、間接的にとることができます。
arrayLength = UBound(array) - LBound(array) + 1
でもこれ、配列の長さが0だとエラーになります。終わってるでしょ。
例外処理が終わってる
「例外処理もできますよ」という顔をしながら、実際はとてつもなくやりにくいです。
「例外が起こるとGoToみたいにラベルジャンプする」というしょうもない機能が付いています。
Sub Hoge()
On Error GoTo error
'なんかの処理
Exit Sub
error:
'例外処理
End Sub
なんか、書き方が違うだけでtry-catch構文みたいなので悪くないように思えますが、もう一度言います。
やってることは「エラーになったらラベルジャンプする」です。
「Exit Sub」なんてつけてますが、(returnみたいに関数を終わらせる)
これがないと普通に下の方に流れて正常な場合も例外処理になります。
色々構文があって、エラー処理した後にもとの行に戻るなんてこともできてしまいますが、所詮はラベルジャンプです。
Sub Hoge()
On Error GoTo error
'なんかの処理
error:
'エラー処理
Resume Next '←次の行から再開させる
End Sub
そして、ラベルジャンプなので複数の例外処理をネストできません。
'※動きません
Sub Hoge()
On Error GoTo error1
'なんかの処理1-1
'なんかの処理1-2
On Error GoTo error2
'なんかの処理2
Exit Sub
error2:
'例外処理2
End Sub
Exit Sub
error1:
'例外処理1
End Sub
もうだめです
goto使っちゃダメっておばあちゃんに言われたでしょ。
プロシージャが終わってる
呼び出し方に統一性がない
まず、VBAにはプロシージャ(関数とかメソッド)が2種類あります。(※本当はもっとあります)
SubとFunctionです。
値を返さないのがSubで、返すのがFunctionです。
書き方も違えば呼び方も違います
Sub サブルーチン
'なんかの処理
End Sub
Sub 引数ありサブルーチン(hooge As integer)
'なんかの処理
End Sub
Function ファンクション As String
'なんかの処理
Set ファンクション = "いろは"
End Sub
Function 引数ありファンクション(hooge As integer) As String
'なんかの処理
Set 引数ありファンクション = "いろは"
End Sub
問題なのは、呼び出し方
返値無しのSubは、()もなにもつけず呼び出します。
引数がある場合も()をつけずに記述します。
サブルーチン
引数ありサブルーチン 123
一方、返値無しのFunctionは、()をつけて記述します
Hoge = ファンクション()
Hoge = 引数ありファンクション(123)
そして、返値を使わない(代入しない)場合は「Call」と書かなければなりません
Call ファンクション()
Call 引数ありファンクション(123)
修正するときただただメンドウ
返値の書き方が独特
C系の言語では、returnで値を返して関数を終わらせます。
VBAでは、値を返す構文と、関数を終わらせる構文は別です。
関数を終わらせるのには「Exit Sub」か「Exit Function」です。
ここでも、SubとFunctionで書き方が違ってメンドウです。
Sub Hoge
Exit Sub
End Sub
Function Fuga As Integer
Exit Function
End Function
返値は、その関数名の変数に代入します。
Function Fuga As Integer
Fuga = 2
Exit Function
End Function
プロシージャの名前を変える場合、この部分も変更しないとエラーになります。
嘘です。デフォルトだと宣言していない変数にも代入できるのでエラーにすらなりません。
※Option Explicitを頭につけましょう。
短絡評価がない
短絡評価とは、複数の真偽判定AとBを組み合わせたときに、
Aの真偽によってはBを判定(=実行)しない仕組みです。
たとえば、あるセルが数式エラー(#N/Aとか)か空文字だったら処理するコードを考えます。
If isError(cell) OR cell.Value = "" Then
MsgBox "セルが数式エラーか空文字だよ"
Else
MsgBox "セルの値は" & cell.Value & "だよ"
End If
上のコード、実はエラーになります。
なぜなら「cell」が数式エラーのセルの場合、
cell.Valueを実行しようとした段階でエラーとなるからです。
上のコードを正しく書くと、以下のようになります。
If IsError(cell) Then
MsgBox "セルが数式エラーか空文字だよ"
Else If cell.Value = "" Then
MsgBox "セルが数式エラーか空文字だよ"
Else
MsgBox "セルの値は" & cell.Value & "だよ"
End If
コードが長くなってしまいました。
短絡評価の機能があれば、とてもシンプルにかけます。
If isError(cell) OrElse cell.Value = "" Then
MsgBox "セルが数式エラーか空文字だよ"
Else
MsgBox "セルの値は" & cell.Value & "だよ"
End If
IsError(cell)が真となった時点で、右側のcell.Valueの値に関わらず全体の評価は真になるので
cell.Valueは実行されない―つまり呼び出せないメソッドを呼ぼうとしてエラーになったりしません。
VBには短絡評価があるそうですが、VBAにはありません。あきらめましょう。
その他色々
変数の宣言と代入が同時にできない。
変数を宣言するだけで、普通の言語の2倍の行を使います。
Dim X As Integer
X = 12
Continueがない
そのままの意味です。For文などでContinueが使えません。
検索するとgoto文使えって言われます。ひどい。
変数のスコープが変(というか、ない)
関数内でスコープが作られません。
なので以下のコードは変数名重複でエラーになります。
If A Then
Dim x As Integer
x= 3
End If
If B Then
Dim x As Integer
x= 10
End If
For文もスコープが作られなません。
そもそもイテレータの宣言を文の外でしなければならない仕様上、イテレータの変数も別名で用意する必要があります。
Dim i As Long
For i = 0 to 10
Dim x As Integer
x= 3
End If
Dim i As Long 'イテレータも被るのでiは無理
For i = 0 to 10
Dim x As Integer 'ここもxは無理
x= 3
End If
Rangeとか何のシートか省略 ”できてしまう”。
なんも気にせず「Range」とか書くと、現在アクティブなエクセルファイルの現在アクティブなシートを参照します。
個人的にはわかりずらいのでやめてほしいです(他人の書いたコード改修する時)
大文字と小文字を区別しない
区別しない上に、VBAの開発環境で勝手に揃えられます。
正規表現に「後読み」がない
詳しくは書きませんが、VBAで使える正規表現のライブラリ、後読みができません
'こういうやつ
(?<=パターン)
(?<!パターン)
そもそもVBAに改善が見込めない
マイクロソフトはもう長い間VBAのメンテナンスをしていません。
後継の言語を実装しようという動きもありません。
つまり、上記の問題点が改善されることをはありません。
一応、ブラウザ版のエクセルには、JSやTypeScriptで書けるOffice Scriptなるものがあるようです。
そちらが逆輸入される日を夢見てます。