※この記事は【VB6・VB】古いコードのリプレース のシリーズその2です。
#その2 旧関数いろいろ
サポートされなくなった関数・今は推奨されない関数の対処まとめです。
今後書き足す予定(2018/11/11現在3まで)
1.Integer,Long【VB6】
2.IIf,And,Or【VB】
3.宣言省略【VB6】
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は違う。
'これは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
'これは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を重ね書きしてる古いソースをたびたび見る。
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で型変換出来ないケースが考慮されてない問題」**とかが埋もれてることに気づけるという副産物もあります。
「既存が動いてるから型変換できないような値は来ないはず!だから大丈夫!」
まぁそうなんですけど……バグの温床は潰すに越したことはないです……ないよね……?
Dim i As Integer 'こっちは省略できない
If Integer.TryParse(q.ToString, i) AndAlso i > 0 Then
'値の評価が終わってもうiに入っている
if (int.TryParse(hoge, out int i) && i > 0) //iの宣言もインライン化できる
//値の評価が終わってもうiに入っている
3.宣言省略【VB6】
意外と面倒なやつ。
VB6では以下のようなコードが許されていた。
Dim hoge() As String
hoge = {"file1", "file2", "file3"}
For i = 0 To 3
hoge(i) = hoge(i) + ".txt"
Next
いや、iって誰だよ!
####対処法:
こういうコードに出会った場合は、1つ1つ中にどういう値が入りうるか、その関数内を全て確認して、問題がなさそうな宣言を入れるべき。
これがiみたいなわかり易い名前ならまだ良いですが、中身が全く推測できないような名前の関数の場合もあるんですよね……
コンバージョンツールなどで置き換えた後、Option Strict Onにすればすぐ見つけることが出来るとはいえ、面倒です。
Dim i As Integer 'このケースならIntegerでいけるだろう
int i; //このケースならintでいけるだろう
調べるのが面倒だからといって、Optionをいじるのは非推奨。【VB】
(私がやってきた案件では絶対NGでした)
Option Explicit Off
'または
Option Strict off
'...
Dim i As Object
c#ならとりあえずvarで宣言して、初期値に最初に使ってる値でも入れとけば多分大丈夫っていう逃げ方も。【c#】
var i = 0; //最初に0が入ってるんだからこれでいいじゃないか
4.配列のサイズ
色んな方法で配列のサイズをいじくるソースがあります。
- Array.Resize【VB6,VB】
- Redim【VB6,VB】
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)にしたい。
特に量が多くて実行速度を考える場合、配列をリサイズするコストはバカにならない。
####実行速度を以下の例で調べてみた。
'サイズ=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()
'*************************************
'■パターン1: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() 'タイマー再開*************************
'■パターン2: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() 'タイマー再開*************************
'■パターン3: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の組み合わせは処理条件自体も一緒に1行で書ける上、
実行速度も数百倍早いです。
ということで使わない理由がありません。
更に、List(Of T)にはAddRangeというメソッドも用意されているので、ラムダ式を組み合わせれば処理全てを1行で書けます。
VB:List(OfT)&AddRange&ラムダ式
i3.AddRange(k.Where(Function(p) p > 0))
>※ただし、ラムダ式は少し実行速度的には劣ります。
>試してみたところ、実行結果はこんな値になりました。
>```:実行結果
List(Of T)&AddRange : 00:00:00.0049192
5倍は時間かかってますね。
けど、これぐらいならソースコードが1行になるメリットのほうが大きいと思います。
####あとたまーにArrayListを使っているソースがあります。
- ArrayList【VB】
Dim p As New ArrayList
p.Add(3)
p.Add("4")
これは.NET FrameWork1.0の頃に出た動的にサイズを変更できる配列的な何かですが、
↑のようにObject型で入るのでかなり使いにくいです。(Object型なので当然、実行速度も遅い)
こっちもList(Of T)に変更することを推奨します。