VB.NET でのお話。
あるプロジェクトを、ある顧客向けに大幅カスタマイズすることになりました。で、コードを見てみると。。。
ケース1
Module ブロック発見!!ざっと眺めると、固定文字列が定義してあるだけっぽい。ならいいや。Public Class に宣言を変更し、すべてのフィールドを Public Const に変更していく。
と、“変数”発見!!やめてよ。これ、ASP.NET ですよ。Module ブロックの変数は、全リクエストで共有されるんですよ。同時に動かされたら、アウトじゃないですか。使用しているクラスを探すと、1つだけだった。なら話は早い。そのクラスのクラス変数として持たせ、コンストラクタで一緒にコンストラクトするように修正。
後で気が付く。こちらのクラスで宣言すりゃいいんじゃないかorz
ケース2
ざっとコードを眺めると、返値宣言していない Function 発見!!やめてってば。嫌な悪寒するけど、Option Strict を On にする。タスクにいっぱい並びました。並びすぎて、「エラーの数が最大値を超えました」なんてメッセージもでてる(涙)
a = b = c = d = 0
って、なんですか?これ、VB.NET ですよ?エラーメッセージは、「Integer である a に Boolean 型は代入できません」。ほぉ!この式は、C 言語風に書くと、
a = (b == (c == (d == 0)))
なんですね!!初めて知りました(涙)
ケース3
当然のように、縮小変換できないというエラーもいっぱいでているわけです。そのうち、一番泣きたかったもの。
Dim 計算結果(maxCount * 10 + 1) As Double
これだけ見ると、おかしくはない。でも、maxCount が Double で宣言されていたら?(泣笑)どうして何かの“数”が、Double なのさ?!
ケース4
コンピュータは、すべてのものを 0 と 1 で表現しようとします。文字だって、“文字コード”という序数で表現され、0 と 1 で表されます。このとき、小数は表現できないものがあります。例えば、0.1。小数は、2進数で 2-n の式で表される数字の和により表現します。つまり、0.5, 0.25, 0.125, 0.0625, 0.03125, ... というような数字の和です。0.1 を、2-n の和で表現することは出来ません。このため、0.1 を 10 回足しても、必ずしも 1 になるとは限りません。近似的に 1 になるので、近似的な 1 を整数の 1 と同じとみなしてくれるかどうか、そこが問題になります。
また、縮小方向に暗黙変換してくれるのならいいのですが、そんなことをすると桁落ちが発生して本当の数字ではなくなるため、拡大方向の暗黙変換のみ、行ってくれます。
そうするとですね、
For cnt As Double = 0 To maxCount Step 0.1
というループは、終了しない可能性があるのです。実際、「終了しないんだけど、なぜ?」という質問があります。
ケース5
Excel などの Office 製品をオートメーションするという案件は、たくさんあると思います。このとき、Option Strict On にしておくと、CType の嵐になります。例えば、
workbook.WorkSheets(sheetName).Range(rangeName) = rangeValue
と書くことは、Strict Off であればとおります。これを、Strict On で書くなら、、、
Ctype(Ctype(Ctype(workbook.WorkSheets, Excel.Sheets)(sheetName), Excel.WrokSheet).Range(rangeName), Excel.Range).Value = rangeValue
と書かなければなりません。
それでもね、やっぱり、Strict On にしましょう。
レイトバインディング、遅延バインディング、実行時バインディングという言葉を聞いたことがありますか?これらは、VB や JavaScrip などで採用されています。実行時にその型がなにで、どんなメンバを持っているのか、解決します。そうするとですね、実行時にいちいち、「このインスタンスの型は何かな?この型にこのメンバは含まれているのかな?」というチェックを行います。こう書くと、「チェックが入る分、遅くなる」ということを、わかってもらえますか?
Strict On にするということは、事前バインディング、アーリーバインディング、コンパイル時バインディングになります。コンパイルの時に型のチェックが行われるため、実行時は参照の解決だけをすればいいので、場合によってはかなり速くなります。今回のプロジェクトでは、10 分以上かかっていた処理が、5 分程度で終わるようになりました。
ケース6
ケース5に引き続き、オートメーションの話。
Insider.NET 会議室に、「Excel のプロセスが終了しない」という質問が、結構あります。かくいう私も、質問した1人です。答え:COM のリリースは Dispose でも GC.Collect でもなく、Marshal.ReleaseComObject メソッドを使用します。
で、これがまたやっかいで。Excel の場合、Excel 名前空間以下に定義されているクラスは、すべてマーシャラーが参照します。したがって、すべて参照を解放してください。ケース5の例を使います。
Dim worksheets As Excel.Sheets = Nothing
Try
worksheets = CType(workbook.WorkSheets, Excel.Sheets)
Dim worksheet As Excel.WorkSheet = Nothing
Try
worksheet = CType(worksheets("シート"), Excel.WorkSheet)
Dim range As Excel.Range = Nothing
Try
range = worksheet.Range("レンジ名")
range.Value = rangeValue
Finally
If Not range Is Nothing Then Marshal.ReleaseComObject(range)
End Try
Finally
If Not worksheet Is Nothing Then Marshal.ReleaseComObject(worksheet)
End Try
Finally
If Not worksheets Is Nothing Then Marshal.ReleaseComObject(worksheets)
End Try
面倒ですね。でも、こうしてください。そうでないと、Excel のプロセスが残ります。
しかし、CType を使ったところと、解放するところが一致していることがわかるでしょうか。また、参照を細かく切ると、実はスピードアップにつながります。
ということで、Option Strict は On で使用しましょう。
投稿日時 : 2006年3月7日 21:45