【VB6・VB】古いコードのリプレース　その２　旧関数いろいろ

**※この記事は[【VB6・VB】古いコードのリプレース](https://qiita.com/so_rei/items/62a0327d321a5e0dd279) のシリーズその２です。**

#その２　旧関数いろいろ

サポートされなくなった関数・今は推奨されない関数の対処まとめです。
今後書き足す予定(2018/11/11現在3まで)

**[1.Integer,Long【VB6】](#1integerlongvb6)**
**[2.IIf,And,Or【VB】](#2iifandorvb)**
**[3.宣言省略【VB6】](#3宣言省略vb6)**
**[4.配列のサイズ](#4配列のサイズ)**

## 1.Integer,Long【VB6】

コンバージョンツールを使っていれば問題はないと思いますが、一応。
VB6のInteger型 = VB・c#のShort型。-32768~32767の範囲。
VB6のLong型 = VB・c#のInteger/int型。

## 2.IIf,And,Or【VB】

VBのIIF、AndとAndAlsoは違う。

```vb:VB：NG
    'これはNG
    Dim number as Integer
    number = IIf(TextBox1.Text = "",0,Integer.Parse(TextBox1.Text))
    'これもNG
    If TextBox1.Text <> "" And Integer.Parse(TextBox1.Text) > 0 Then
        number = Integer.Parse(TextBox1.Text)
    End If
```

```vb:VB：OK
    'これはOK
    number = If(TextBox1.Text = "",0,Integer.Parse(TextBox1.Text))
    'これもOK
    If TextBox1.Text <> "" AndAlso Integer.Parse(TextBox1.Text) > 0 Then
        number = Integer.Parse(TextBox1.Text)
    End If
```

####理由：
IIF関数はTrueのときもFalseの時の式を評価してしまう。
And・Or演算子は前がTrueでなくとも2つ目の式を評価してしまう。
なのでTextBox1.Text=""のときもInteger.Parse()を評価してしまいエラーになる。

AndAlso・OrElse演算子ならそういうことは起きないため安全。
AndAlso・OrElseを使わないためだけにひたすらIfを重ね書きしてる古いソースをたびたび見る。

```vb6.0:VB6：古いソースの例
   Dim hoge As String

   'hogeに値を入れる......

   If IsNothing(hoge) = False Then
        If hoge <> "" Then
            Dim i As Integer
            i = CInt(hoge)
            If i > 0 Then
                'ここからようやく値の評価が始まる
```

こういうのはさっさと書き換えましょう。
書き換えていると、例のように**「CIntで型変換出来ないケースが考慮されてない問題」**とかが埋もれてることに気づけるという副産物もあります。

*「既存が動いてるから型変換できないような値は来ないはず！だから大丈夫！」*
まぁそうなんですけど……バグの温床は潰すに越したことはないです……ないよね……？

```vb:VB：変更後
    Dim i As Integer 'こっちは省略できない
    If Integer.TryParse(q.ToString, i) AndAlso i > 0 Then
        '値の評価が終わってもうiに入っている
```

```c#:C#：変更後
    if (int.TryParse(hoge, out int i) && i > 0) //iの宣言もインライン化できる
        //値の評価が終わってもうiに入っている
```


## 3.宣言省略【VB6】

意外と面倒なやつ。

VB6では以下のようなコードが許されていた。

```vb6.0:VB6：古いソースの例
        Dim hoge() As String
        hoge = {"file1", "file2", "file3"}
        For i = 0 To 3
            hoge(i) = hoge(i) + ".txt"
        Next        
```

**いや、iって誰だよ！**

####対処法：
こういうコードに出会った場合は、１つ１つ中にどういう値が入りうるか、その関数内を全て確認して、問題がなさそうな宣言を入れるべき。

これがiみたいなわかり易い名前ならまだ良いですが、中身が全く推測できないような名前の関数の場合もあるんですよね……
コンバージョンツールなどで置き換えた後、Option Strict Onにすればすぐ見つけることが出来るとはいえ、面倒です。

```vb:VB：OK
    Dim i As Integer 'このケースならIntegerでいけるだろう
```

```c#:c#：OK
    int i; //このケースならintでいけるだろう
```


調べるのが面倒だからといって、Optionをいじるのは非推奨。【VB】
(私がやってきた案件では絶対NGでした)

```vb:VB：非推奨
    Option Explicit Off
    'または
    Option Strict off
    '...
    Dim i As Object
```

c#ならとりあえずvarで宣言して、初期値に最初に使ってる値でも入れとけば多分大丈夫っていう逃げ方も。【c#】

```c#:c#：多分大丈夫
    var i = 0; //最初に0が入ってるんだからこれでいいじゃないか
```

## 4.配列のサイズ

色んな方法で配列のサイズをいじくるソースがあります。

- Array.Resize【VB6,VB】
- Redim【VB6,VB】

```vb6.0:古いソースの例
        Dim i(3) As Integer
        Dim k(100) As Integer

        'kにいろいろ値を入れる...

        'kのうち、正の値のものだけ配列iの後ろに付け足す
        Dim iNewSize As Integer
        For kLoop As Integer = 0 To k.Count - 1
            If k(kLoop) > 0 Then
                'Array.Resizeの場合
                iNewSize = i.Length + 1
                Array.Resize(i, iNewSize)

                'Redim Preserveの場合
                iNewSize = i.Length
                Redim Preserve i(iNewSize)

                i1(iNewSize - 1) = k(kLoop)
            End If
        Next
```

こういうのは出来れば全部List(Of T)にしたい。
特に量が多くて実行速度を考える場合、配列をリサイズするコストはバカにならない。

####実行速度を以下の例で調べてみた。

``` VB:VB：Array.Resize／Redim／List(OfT)の実行速度確認
        'サイズ=3で初期化
        Dim i1(3) As Integer
        Dim i2(3) As Integer
        Dim i3 As New List(Of Integer)
        i3.AddRange({0, 0, 0, 0})

        'kにいろいろ値を入れる...
        Dim k(100000) As Integer
        Dim r As New Random
        For i As Integer = 0 To k.Count - 1
            k(i) = r.Next(0, 10)
        Next

        'タイマースタート*********************
        Dim sw As New System.Diagnostics.Stopwatch()
        sw.Start()
        '*************************************

        '■パターン１：Array.Resize
        'kのうち、正の値のものだけ配列iの後ろに付け足す
        Dim iNewSize As Integer
        For kLoop As Integer = 0 To k.Count - 1
            If k(kLoop) > 0 Then
                iNewSize = i1.Length + 1
                Array.Resize(i1, iNewSize)
                i1(iNewSize - 1) = k(kLoop)
            End If
        Next

        'タイマーストップ*********************
        sw.Stop()
        Debug.Print("Array.Resize : " + sw.Elapsed.ToString)
        '*************************************

        sw.Restart() 'タイマー再開*************************

        '■パターン２：Redim Preserve
        'kのうち、正の値のものだけ配列iの後ろに付け足す
        Dim iNewSize2 As Integer
        For kLoop As Integer = 0 To k.Count - 1
            If k(kLoop) > 0 Then
                iNewSize2 = i2.Length
                ReDim Preserve i2(iNewSize2)
                i2(iNewSize2 - 1) = k(kLoop)
            End If
        Next

        'タイマーストップ*********************
        sw.Stop()
        Debug.Print("ReDim Preserve : " + sw.Elapsed.ToString)
        '*************************************

        sw.Restart() 'タイマー再開*************************

        '■パターン３：List(Of T)とAddRange
        'kのうち、正の値のものだけ配列iの後ろに付け足す
        For kLoop As Integer = 0 To k.Count - 1
            If k(kLoop) > 0 Then
                i3.Add(k(kLoop))
            End If
        Next

        'タイマーストップ*********************
        sw.Stop()
        Debug.Print("List(Of T) : " + sw.Elapsed.ToString)
        '*************************************
```

####実行結果

```:実行結果
Array.Resize : 00:00:03.2530918
ReDim Preserve : 00:00:06.0734898
List(Of T) : 00:00:00.0009194
```

見ての通り、List(Of T)＆Addの組み合わせは処理条件自体も一緒に１行で書ける上、
実行速度も数百倍早いです。
ということで使わない理由がありません。

>**更に、List(Of T)にはAddRangeというメソッドも用意されているので、ラムダ式を組み合わせれば処理全てを１行で書けます。**

>```vb:VB：List(OfT)&AddRange&ラムダ式
        i3.AddRange(k.Where(Function(p) p > 0))
```

>※ただし、ラムダ式は少し実行速度的には劣ります。
>試してみたところ、実行結果はこんな値になりました。

>```:実行結果
List(Of T)&AddRange : 00:00:00.0049192
```

>５倍は時間かかってますね。
>けど、これぐらいならソースコードが１行になるメリットのほうが大きいと思います。



####あとたまーにArrayListを使っているソースがあります。

- ArrayList【VB】

```vb:VB：ArrayListの例
    Dim p As New ArrayList
    p.Add(3)
    p.Add("4")
```

これは.NET FrameWork1.0の頃に出た動的にサイズを変更できる配列的な何かですが、
↑のようにObject型で入るのでかなり使いにくいです。(Object型なので当然、実行速度も遅い)
こっちもList(Of T)に変更することを推奨します。