ExcelVBA 　注意すべきこと

::: note warn
自分用のメモなので、形は整ってないです。
:::
### Cells(1, 1)は必ずしもA1セルを指すとは限らない。Range指定がある場合は、Cells(1, 1)はRangeの左上セルを指す
```
Sub qqq()

Range(Cells(1, 1), Cells(10, 1)).Value = "AAA"  'A列
Range(Cells(1, 2), Cells(10, 2)).Value = "BBB"  'B列

Debug.Print Range("B1:B10").Cells(1, 1).Value
'これは "BBB" になる。Range指定がある場合は、Cells(1, 1)はRangeの左上セルを指す
'通常はA1セルになるが、Range指定がある場合は注意

'Rangeオブジェクトに対して、Cells(*,*)でセル取得すると、正しいアドレスにならないかも？
Debug.Print Range(Cells(1, 1), Cells(10, 3)).Cells(1, 1).Address
Debug.Print Range(Cells(1, 1), Cells(10, 3)).Cells(3, 2).Address

'Rangeに対しては以下のようにしたほうが確実な気がする
Debug.Print Range(Cells(1, 1), Cells(10, 3))(1, 1).Address
Debug.Print Range(Cells(1, 1), Cells(10, 3))(3, 2).Address


Debug.Print Cells.[_Default](1, 1)
'ちなみに、これでRangeオブジェクトのデフォルトプロパティ(Value)を取得できる
'Valueは省略しても取得できるが、正確には[_Default]を取得している

End Sub
```
### シートをコピーする場合の注意点　　Set wsX = ActiveSheet というコードは、間違いが起きやすいので使わないように　　非表示のシートも注意
```
Sub aaa()

Dim wsX As Worksheet

Worksheets("シート1").Copy After:=Worksheets(Worksheets.Count)
'「シート1」をコピーし、末尾に移動する
Set wsX = Worksheets(Worksheets.Count)
'コピー直後に末尾のシートをSetすれば、ほぼ間違いなくコピーしたシートが対象になるはず
wsX.name = "コピーしたシート"

'※ Set wsX = ActiveSheet というコードは、間違いが起きやすいので使わないように


'*****　非表示のシートについて  **************

'Worksheets("隠しシート").Copy   非表示のシートの場合、このコードはエラーになる

Worksheets("隠しシート").Visible = xlSheetVeryHidden  'VBAでしか再表示できないように非表示にする
'Worksheets("隠しシート").Visible = xlSheetHidden      ' これは普通に非表示

If Worksheets("隠しシート").Visible = xlSheetHidden Or _
  Worksheets("隠しシート").Visible = xlSheetVeryHidden Then
  '非表示シート、またはVBAでしか再表示できないように非表示になっている場合
  Worksheets("隠しシート").Visible = xlSheetVisible
End If

Worksheets("隠しシート").Copy After:=Worksheets(Worksheets.Count)
Set wsX = Worksheets(Worksheets.Count)
wsX.name = "コピーした隠しシート"
'ここまでやれば安全だろうか

End Sub
```
### VBAの関数とワークシート関数では、同名の関数でも処理が違うことがある(Roundが有名)　　ワークシート関数にあわせるのが無難か
```
Debug.Print Application.WorksheetFunction.Round(2.5, 0)  '3 になる
Debug.Print Round(2.5, 0)  '2 になる

'ワークシート関数ROUNDは、通常の四捨五入処理をする
'VBA関数ROUNDは、銀行型の丸め処理をするので、整数部1桁の数値により四捨五入の仕方が変わる

Debug.Print "----------------"

Debug.Print Application.WorksheetFunction.Round(1.5, 0)  '2 になる
Debug.Print Round(1.5, 0)  '2 になる
```
### オブジェクト型の変数を宣言する場合は、Newを使わないようにするべき。バグの原因になる
```
Sub aaa()

Dim col1 As Collection

Set col1 = New Collection
col1.Add "AAA"

Set col1 = Nothing  'ここでNothingにする


On Error Resume Next
col1.Add "BBB"  'このコードはエラーになる
If Err.Number <> 0 Then MsgBox "エラー"

Stop  'col1の内容はNothing
On Error GoTo 0


Dim col2 As New Collection  'Newで宣言

col2.Add "AAA"

Set col2 = Nothing  'ここでNothingにする


On Error Resume Next
col2.Add "BBB"  'このコードはエラーにならない
If Err.Number <> 0 Then MsgBox "エラー"

Stop  'col2の内容は、"BBB" だけが入っている

'オブジェクト化多の変数をNewで宣言した場合、インスタンスが生成されるのは宣言時ではなく、
'変数が使用される時になるらしい。Newで宣言するのはバグの原因になる

End Sub
```
### コントロール(リストボックス等)の、ValueとTextの違い　　ValueはNullを取り得る
[リンク](http://officetanaka.net/excel/vba/tips/tips143.htm)

### 1セルだけの範囲をVariant型に入れて配列にした場合、配列にはならない。さらにその1セルが空白だと、Empty値になるらしい。1セルだけも、空白でなければEmpty値にならにない。複数のセル範囲だと、空白セルだけでもEmptyにならないみたい
```
Sub kkk()

'1セルだけの範囲をVariant型に格納した場合、配列にならない
'その1セルが空白だと、Empty値になるらしい
'1セルだけも、空白でなければEmpty値にならにない
Dim var1 As Variant
Cells(1, 1).Value = ""
var1 = Range(Cells(1, 1), Cells(1, 1)).Value  '1セルのみの範囲で、そのセルが空白
Debug.Print VarType(var1)  '0  Empty値

Cells(1, 1).Value = 500
var1 = Range(Cells(1, 1), Cells(1, 1)).Value  '1セルのみの範囲で、そのセルが空白
Debug.Print VarType(var1)   '5 　Doubleの数値

'配列ではないので、以下のコードはエラーになることに注意
'Debug.Print UBound(var1, 1)


'複数のセル範囲だと、空白セルだけでもEmptyにならないみたい
Dim var2 As Variant
Cells(1, 1).Value = ""
Cells(2, 1).Value = ""
var2 = Range(Cells(1, 1), Cells(2, 1)).Value
Debug.Print VarType(var2)  '8204

'セル範囲をVariantに格納するときは、VarTypeで判定するといいかも

End Sub
```
### セルの書式を通貨型にすると、VBA内では、小数部は4桁目までの四捨五入丸めになってしまうらしい
```
0.123456 → 0.1235 になる　　セルの表示は4桁目以降も出ているし、値も同じだが、VBA内での扱いが違うか

Sub nnnn()

Cells.Clear
Cells(1, 1).NumberFormatLocal = "\#,##0.000000;\-#,##0.000000"
'セルの書式を通貨型にする 小数部分は6桁

Cells(1, 1).Value = 0.123456789
Debug.Print Cells(1, 1).Value   '0.1235
'セルの書式を通貨型にすると、小数部は4桁目までの四捨五入丸めになってしまうらしい

Debug.Print Cells(1, 1).Value = 0.1235   'True


Cells(1, 1).Value = 0.1
Debug.Print Cells(1, 1).Value   '0.1
Cells(1, 1).Value = 0.12
Debug.Print Cells(1, 1).Value   '0.12
Cells(1, 1).Value = 0.123
Debug.Print Cells(1, 1).Value   '0.123
Cells(1, 1).Value = 0.1234
Debug.Print Cells(1, 1).Value   '0.1234
Cells(1, 1).Value = 0.12345
Debug.Print Cells(1, 1).Value   '0.1235


Debug.Print VarType(Cells(1, 1))   '6  通貨型　　Doubleとは判定されないらしい。これで判断すべきか
End Sub
```
### vbCrLf、vbLf、vbCrを互いに置換する場合は注意が必要。LFをCRLFに置換すると、CRLFはCRCRLFになってしまう
```
Sub kkkk()

Dim str1 As String
str1 = "aa" & vbCrLf & "bb" & vbLf

str1 = Replace(str1, vbLf, vbCrLf)  'LFをCRLFに置換

Debug.Print str1 = "aa" & vbCrLf & "bb" & vbCrLf         'False
Debug.Print str1 = "aa" & vbCr & vbCrLf & "bb" & vbCrLf  'True
'どうしてこうなるかというと、LFをCRLFに置換すると、CRLFはCRCRLFになってしまうかららしい

'CRLF LF CRが混在する文字列をCRLFで統一するには、以下のように全てLFにしてからCRLFにすると分かりやすいかも
str1 = "aa" & vbCrLf & "bb" & vbLf & "cc" & vbCr & "dd" & vbCrLf
str1 = Replace(str1, vbCrLf, vbLf)
str1 = Replace(str1, vbCr, vbLf)
str1 = Replace(str1, vbLf, vbCrLf)
Debug.Print str1 = "aa" & vbCrLf & "bb" & vbCrLf & "cc" & vbCrLf & "dd" & vbCrLf  'True

End Sub
```
### セル内の一部だけのフォント設定(フォント色等)を変更している場合、セルのフォント設定を取得するとNull値がリターンされるので注意　　フォント色・太字・フォントサイズ・フォント名・下線付き・斜め字体などは全てNullになってしまう
```
Sub vvv()

' Cells(1, 1)のテキストは、一部だけを赤のフォント色にしてある
Debug.Print Cells(1, 1).Font.Color            '結果はNull
Debug.Print VarType(Cells(1, 1).Font.Color)   '1  Null値


' Cells(2, 1)のテキストは、一部だけを「メイリオ」フォントにしてある
Debug.Print Cells(2, 1).Font.name            '結果はNull
Debug.Print VarType(Cells(2, 1).Font.name)   '1  Null値


' Cells(3, 1)のテキストは、一部だけを太字フォントにしてある
Debug.Print Cells(3, 1).Font.Bold            '結果はNull
Debug.Print VarType(Cells(3, 1).Font.Bold)   '1  Null値


' Cells(4, 1)のテキストは、一部だけを20サイズのフォントにしてある
Debug.Print Cells(4, 1).Font.Size            '結果はNull
Debug.Print VarType(Cells(4, 1).Font.Size)   '1  Null値


' Cells(5, 1)のテキストは、一部だけを下線付きにしてある
Debug.Print Cells(5, 1).Font.Underline            '結果はNull
Debug.Print VarType(Cells(5, 1).Font.Underline)   '1  Null値


' Cells(6, 1)のテキストは、一部だけを斜め字体のフォントにしてある
Debug.Print Cells(6, 1).Font.Italic            '結果はNull
Debug.Print VarType(Cells(6, 1).Font.Italic)   '1  Null値


'セル内の一部のテキストだけが変更されている場合、フォントの各種設定を取得すると全てNullが返されることになるようだ

End Sub
```