[備忘録]C#出身の僕がExcel VBAで悩んだところ

# 背景

Excel VBA を書く機会がたまにあるのですが，C#やpython出身の僕がVBAを書いていて悩んだところを備忘録として残します．

# 悩んだところ

## コメントが  ' である

```vbnet
' がコメントなんです
' #でも //でもありません
```

## 変数の定義は強制されない

VBAではデフォルトで変数の定義をせずに使えてしまうので（pythonやrubyと同じ），複雑なコードを書いていく場合は

```vbnet
Option Explicit

a = 1 ' エラーになる
Dim a As Integer
a = 1 ' エラーにならない
```
Option Explicit で定義を強制しましょう

## 変数定義時に値を代入できない

```vbnet
' 変数は定義と同時に代入できません
Dim a As Integer
a = 10
' 定数は定義時にしか代入できない
Const b As Integer = 10
```

## return がない

```vbnet
Public Function RetStr()
    RetStr = "返り値" ' メソッド名に値を代入すると返り値になる
End Function
```

## 返り値の型の指定の仕方

```vbnet
Public Function Hoge(str As String) As Integer
    Dim i As Integer
    i = 1
    Hoge = i
End Function
```

## 返り値のないメソッドでは () を使わない

```vbnet

Public Function RetStr()
    RetStr = "返り値がある"
End Function

Public Sub NonRetStr()
    Debug.Print "返り値が無い"
End Sub


Public Function Hoge()
    Dim a As String
    a = RetStr() ' 返り値があるメソッドの呼び出し
    Debug.Print a
    
    Call NonRetStr '返り値が無いメソッドの呼び出し
End Function
```

## ++が使えない

```vbnet
Dim idx As Integer
idx = 0
idx = idx + 1
```

## 同じ型の変数を同時に定義できない

```vbnet
' 以下の定義だと a は Variant型になります
Dim a, b As Integer
' 両方ともIntegerにするには以下の通りです
Dim a As Integer, b As Integer
```

## 値型と参照型では代入の仕方が異なる

```vbnet
' 値型の代入
Dim a As Integer, b As Integer
a = b
' c と dが参照型である場合 Set を使う
Dim c As Worksheet, d As Worksheet
Set c = d
```
参照を意識すると言うのは，僕は大切だと思うのですが個人的には好きなのですが，

```vbnet
c = d
```
とすると例外メッセージ「オブジェクト変数またはWithブロック変数が設定されていない」が

![ref_insert.PNG](https://qiita-image-store.s3.amazonaws.com/0/36752/fa162cdc-8ae3-a6cf-52d7-90548e1c57c6.png)

と出てきて，理解しにくいのが難点です．

## For i = 1 To 1 でも式の中に入ってしまう

![for.PNG](https://qiita-image-store.s3.amazonaws.com/0/36752/80dddd58-dc94-b862-09a0-20e99a95fea1.png)

もし入らないようにしたければ、Whileを用いましょう

## Continueがない
For文内でcontinueする場合は GoToを用いましょう

![for_continu.PNG](https://qiita-image-store.s3.amazonaws.com/0/36752/d4bc6408-40f3-0d9f-cff9-265a864a7659.png)

## ForやIf内でスコープがない

SubやFunctionの中で宣言した変数は，その中でしか使えないというスコープを持っていますが，For文やIf文などの中で宣言した変数はSub内Function内で使えてしまいます。
例えば For文用のIndexを定義すると、そのIndexはモジュール内のどこからでもアクセスできてしまいます（単語補完でも表示されます）

![scope.PNG](https://qiita-image-store.s3.amazonaws.com/0/36752/dabc6d49-15ce-4218-daeb-6d7d1a3a45fc.png)

## Dim で初期化されない
まず、Dimで二度同じ変数を宣言するとエラーを吐きます

```vbnet
Dim a(1 To 10)
Dim a(1 To 10) ' エラー
```

しかし，For文ではエラーになりません

```vbnet
Dim idx as integer
For idx = 1 To 10
     Dim a(1 To 10) ' エラーにならない
Next idx
```

しかし、このaは初期化されておらず、前に格納された値を保持します

```vbnet
Dim idx as integer
For idx = 1 To 10
     Dim a(1 To 10)
     a(idx) = idx
Next idx 
' aは1から10の値を持った配列となる．不思議！
```

## 配列のIndexが1から始まる場合と0から始まる場合がある

```vbnet
Dim a As Variant
a = Array(1,2,3) ' indexは 0,1,2
ReDim a(1 To 3) ' indexは 1,2,3
```

Option Base 1を先頭につけることで、Array()で生成される配列のindexを1から始められるようになります．
（こちらのほうがセルの参照時などに相性がいいのでお勧めです）

```vbnet
Option Base 1 ' defaultでは 0
```

## 細かな話（時間があれば，きちんとまとめます）

### WindowsとMacで使えるメソッドと引数が決まっている
特にファイル選択など入出力系ではMac版は不便で、Apple Scriptを使わなければダメです

### WorkSheetのActivateの注意点
Sheet(“シート名”).Activate でアクティベーション（参照などが行える）できますが、アクティベートしたSheetのWithコンテキストの中で他のシートに書き込みは出来ないので注意です

### Emptyの値が0を取る
Cellが空であるかどうかをちゃんとIsEmptyで確認しないと、普通に空のセルの数値を０として計算が通ってしまいます．

### ソートがないため、自分で実装する

C#などでは arr.OrderBy()で簡単にソートできるのですが，そう言ったデータ配列をソートする関数は標準で用意されていないようです．
（ご存知の方はご教示ください．．．）

### Dictionary, Collectionなどの型は他のDllを参照する必要があり、デフォルトでは使えない

これらは複雑なコードを書いていく上で必須ですので，dllを参照しましょう．
![ref.PNG](https://qiita-image-store.s3.amazonaws.com/0/36752/9bc28c42-5eeb-ade8-7aa7-b59034999d04.png)





# 勉強法
Excel はビジュアライゼーションと操作性が優れているので便利なのですが，VBAには色々つまずきました．
VBAを学ぶのであれば，まずマクロの記録をしてみて，それで記録されたマクロがどういうコードなのかを理解することです．
あとは，ググりましょう！