.NET開発者中心 厳選ブログ記事 .NET開発を始めるVB6プログラマーが知るべき9のこと 2011/05/11 |
「.NET開発者中心 厳選ブログ記事」シリーズでは、世界中にある膨大なブログ・コンテンツの中から、特にInsider.NET/.NET開発者中心の読者に有用だと考えられるブログ記事を編集部が発掘・厳選し、そのブログ記事を執筆したブロガーの許可の下、その全文を転載・翻訳しています。この活動により、.NET開発者のブログ文化の価値と質を高め、より一層の盛り上げに貢献することを目指しています。 |
本稿は、ブログ記事「まさるDiary: VBプログラマが知るべき9のこと by @masaru_b_cl」に簡単な校正・加筆を行ったうえで転載したものです。 |
この記事に書いてあることは目新しいことでも何でもなく、これまで各所でいろいろといわれていたこと(下記の9点)をまとめたものである。
- Option Strictを「On」に
- On Errorは使わない
- Form.Show()はしない
- ByVal/ByRefを明示する
- 文字列連結は&で行う
- プロシージャ呼び出しの()は略さない
- Callを使用しない
- 長大なWithは避ける
- 無意味なNothingの代入
なお、単に「VB」と表記しているものは、.NET以降の「Visual Basic .NET 2002/.NET 2003/2005/2008/2010」などを意味している。
■1. Option Strictを「On」に
まず真っ先にしてほしいのが、「Option Strict」を「On」にすることである。
Option Strictは明示的にOnにしないと、既定ではOffと見なされるため、次のようなコードが書けてしまう。
Dim str As String |
上記のコードでは変数の宣言と値の設定が近い位置にあるため、文字列を扱っていることがすぐに分かるが、実際現場で目にするような長大なコード*1では、その変数が文字列を扱うのか数値を扱うのか即座に判別できないため、保守が苦痛になる。
*1 場合によっては数百〜千行を超える。 |
また、次のようなコードも書けてしまう。
Dim str As String |
Console.WriteLineメソッドでコンソールにどんな値が出力されるか、すぐに分かるであろうか?*2
*2 上記のコードの「"12"」が数値として扱われるため、正解は「36」になる。 |
Option StrictをOnにすることで、上記のようなコードはすべてコンパイル・エラーとなる。
では、どうやってOption Strictを設定するかというと、わたしはVisual Studioの[オプション]ダイアログで、[Visual Basicの既定値]の設定を変更してしまうことを勧める(下の画面を参照)*a。
*a 【注記】元記事に対するコメントの引用: |
Visual Studioの設定だけだと、ほかの人がコードを編集した際にバグが混入する恐れがあります。コードの先頭に以下の2行を書くのは必須ですね。これならVisual Studioの設定に左右されません。 Option Explicit On Option Strict On |
■2. On Errorは使わない
VB6時代のエラー・ハンドリング用構文である「On Error 〜」は現在のVBでも使用できる。
On Error ステートメントを使用しないと、発生するすべてのランタイム エラーが致命的なエラーになります。つまり、エラー メッセージが表示され、実行が停止します。
Sub Main() |
だが、On Errorはあくまで「プロシージャ単位」でしかエラーのハンドリングができないうえ、特定のエラーだけハンドリングするといったこともできない。VB6時代のソース・コードをほとんど流用するといった特殊な場合でもない限り、構造化例外処理を使用すべきである。
以下に構造化例外処理を使った例を示す。
Public Sub Main() |
構造化例外処理では、Finallyブロックを使用して共通の後処理が行えることも大きな特徴である。
ただし、構造化例外処理もプロシージャ単位で行っていてはOn Errorとほとんど変わらない。プロシージャでは原則として例外のCatchは行わず、「集約例外ハンドラ」(後述)で行うようにし、後処理が必要なものは、Finallyブロックで行うことを勧める。
以下に、コンソール・アプリケーションで集約例外ハンドラを用いた例外処理の例を示す。
Sub Main() |
なお、.NET言語の例外処理については、以下のブログ・エントリが詳しいのでぜひ一読してほしい(コードはC#だが意味は通じるはず)。*3
- とあるコンサルタントのつぶやき:.NETの例外処理 Part.1
- とあるコンサルタントのつぶやき:.NETの例外処理 Part.2
- とあるコンサルタントのつぶやき:.NETの例外処理 Part.3
- とあるコンサルタントのつぶやき:.NETの例外処理 Part.4
*3 上記のコードも、これらのブログ・エントリを参考にさせていただいた。 |
■3. Form.Show()はしない
Windowsフォーム・アプリケーションで、ほかのウィンドウを開きたい場合、通常は以下のように記述する。
Dim subForm As New SubForm() |
しかし、VBは次のような記述もできてしまう。
SubForm.Show() |
もちろん、C#で同じように書いたらコンパイル・エラーとなる。
では、なぜVBではコンパイル・エラーとならず、しかも動作してしまうのか? それは「(Formクラスの)既定のインスタンス」と呼ばれる機能が関係している。この機能は、Visual StudioでWindowsフォーム・アプリケーションを作成する際、「プロジェクトに含まれるFormクラス」を自動的にインスタンス化し、My.Formsオブジェクトのプロパティとして公開するものだ。
つまり、上記のコードは、SubFormクラスの静的なShowメソッドではなく、My.FormsオブジェクトのSubFormプロパティを通じて、SubFormクラスのインスタンスを取得して、そのShowメソッドを呼び出していることになる。
この機能はVB6時代と同じ書き方ができるように導入されたものと推測するが、Formクラスのインスタンスが「どこ」に属するのかを意識しないため、予期せぬバグを生みやすい。よって、Formクラスは明示的にインスタンス化してShowメソッドを呼ぶようにした方がよい。
なお、「(Formクラスの)既定のインスタンス」を抑止する方法もあるようである。詳しくは以下のブログ・エントリをコメントまで含めて参照していただきたい。
■4. ByVal/ByRefを明示する
VBでは、プロシージャの引数に対してByVal(値渡し)やByRef(参照渡し)などの修飾子を指定できる。
例を挙げると次のようになる。
Private Sub Hoge(ByVal a As Integer, ByRef b As Integer) |
ByVal修飾子を指定した引数に値を渡しても、呼び出し元の変数の値は変わらないが、ByRef修飾子を指定した場合、呼び出し元の変数の値が書き換わる。
この、「ByVal」「ByRef」を省略するとどうなるかというと、既定で「ByVal」として扱われる。*4
*4 VB6時代は「ByRef」として扱われていた。 |
しかし、一見してどちらの動きとなるか分かりづらいため、ByVal修飾子やByRef修飾子は省略せずに書くべきである*b。
*b 【注記】元記事に対するコメントの引用: |
(いつのバージョンからかはいま正確に分かりませんが)VB 2008以降のバージョンなら「コードの再フォーマット」を無効にしない限り、「ByVal」を省略して書こうとしても強制的に付く仕様になっていますね。 ※編集部注:逆に、VB 2010 SP1では、再び「ByVal」が自動的に付かない仕様に変更になっています(参考:「さようなら、ByVal: 放課後のVB中学校」)。 |
なお、ByVal修飾子とByRef修飾子について、より詳しく知りたい場合は、以下の記事が参考になる。
■5. 文字列連結は&で行う
VBで文字列を連結するには、「+演算子」か「&演算子」を使う。
Dim a As String = "1" + "2" |
ただ、「+演算子」は数値演算でも使用するため紛らわしい。特に「Option Strict Off」と組み合わさると凶悪さを増すことは前述のとおり。
従って、文字列を連結する場合は、常に「&演算子」を使ってもらいたい。
なお、相当数の文字列をループで連結するような場合*5、演算子による連結ではなく、StringBuilderクラスを使用すべきであるので、併せて覚えておいてもらいたい。
*5 CSVフォーマットのテキストを生成する、などが考えられる。 |
■6. プロシージャ呼び出しの()は略さない
VBでプロシージャ(=メソッド)を呼び出す際、引数がない場合は(メソッド名の後に続く)「()」を省略できる。しかし、メソッドをこのように記述するとプロパティと紛らわしいため、「()」を省略してはいけない。
Private Function Hoge() As String |
なお、Visual Studioでコードを書く場合は、自動的に「()」が補完される。
■7. Callを使用しない
VBでプロシージャを呼び出す方法として、Callステートメントがある。
Private Function Hoge(ByVal a As String, ByVal b As String) As String |
だが、「Call」は付けても付けなくても、結果は同じである。わざわざ付ける必要はない。*6
*6 わたしの考え。人によっては「Call」を付けた方が見やすいと言われることもある。 |
■8. 長大なWithは避ける
VBの特徴的な記法として、Withステートメントがある。
Dim product As New Product() |
「With」を使うことで、特定のオブジェクトのメンバへのアクセスをまとめて記述できる。
だが、入れ子にしたり、Withステートメントの中で関係のない処理をしたり、ほかのステートメントと合わさったりすると、急に凶暴性を発揮する。
Dim org As New Organization() |
コード例に示したように、「.〜」で指定したメンバが、どのインスタンスのものか非常に分かりにくくなってしまう。*7
*7 しかし、入れ子となっている親「With」のメンバに、子「With」の中から「.〜」という記述でアクセスすることはできない。 |
Withステートメントを使う場合、そのコードが1画面の表示範囲に収まる程度の長さにし、極力、入れ子にしないよう心がけていただきたい。
■9. 無意味なNothingの代入
VB6以前では、以下のように変数に「Nothing」を代入することで、オブジェクトを破棄していた。
Set hoge = Nothing |
その延長なのか、メソッドの終わりで変数に「Nothing」を代入しているソース・コードをよく見かける。
Sub Main() |
このような記述はVB.NET以降では「意味がない」。なぜならNothingを代入してもオブジェクトは破棄されないからである。*8
*8 オブジェクトの破棄はGC(ガベージ・コレクタ)によって自動的に行われる。ごくまれに、GCに回収されやすくするために、「Nothing」を代入することもあるが、そんなのはレアケース。 |
また、参照型でなく値型の変数に「Nothing」を代入した場合、暗黙的にその型の初期値が設定される。
Dim i As Integer = Nothing ' 0 |
そんな「暗黙の既定値」に頼ったコードは可読性も悪く、思わぬバグも生みかねない。従って、まったく意味がないうえに余計な混乱を招く可能性がある「Nothing」の代入は行わないようにしてもらいたい。
なお、IDisposableインターフェイスを実装した型は、Disposeメソッドを呼び出すことで、明示的に「アンマネージ・リソース」を解放する。
Usingステートメントと併用して、原則的にDisposeメソッドを必ず呼ぶようにしてもらいたい。
Using conn As new SqlConnection() |
■まとめ
VB6以前とVB.NET以降は「別の言語」。各言語に沿った正しい記述を心がけよう。
【筆者プロフィール】 新潟県長岡市在住の子育て奮闘中プログラマー。家事や仕事の合間を縫って、Blogやコミュニティ活動を通して情報発信を行う。著書に『かんたんASP.NET』(技術評論社、2010年)。 |
「.NET開発者中心 厳選ブログ記事」 |
- 第2回 簡潔なコーディングのために (2017/7/26)
ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている - 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう - 第1回 明瞭なコーディングのために (2017/7/19)
C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える - Presentation Translator (2017/7/18)
Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
|
|
- - PR -