VBAがおわっているという話

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型に似てる…かも。

しかし、普通に代入した型のメソッドを呼び出せるのでC#のdynamic型のような挙動もします。

なんか気持ち悪いね。

参照型はSetで代入する。

数値型とかのプリミティブ(?)な型はそのまま代入します。

X = 10

参照型はSetをつけないと実行時エラーになります。

Set X = Range("A1:Z10")

参照型と値型分けるのはいいんですけど、こんな型ゆるゆるなのにそこ気にする?という感想です。

配列がおわっている

上の例で配列を使いましたが、いろいろと構文が気持ち悪いです。

まず、要素のアクセスに()を使うのが個人的に好きではありません

が、言語の書き方の問題。

郷にいては郷に従えということで、目をつむるとします。

でも、添え字が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

もうだめです

ラベルジャンプ使っちゃダメっておばあちゃんに言われたでしょ。

プロシージャが終わってる

呼び出し方に統一性がない

まず、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

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などで変数のスコープが作られません。

なので以下のコードは変数名重複でエラーになります。

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なるものがあるようです。

そちらが逆輸入される日を夢見てます。