連載:熱血VBプログラマ応援団


第3回 VBがC#やJavaよりもさえている1つの事例

―― 他言語では陥りやすい問題を言語仕様レベルで回避済み! ――

株式会社ピーデー 川俣 晶
2004/04/28
 

− 今回のご相談 −

 あなたは、Visual Basicこそこれから先の本命のプログラム言語とおっしゃっていますが、本当にVisual Basicはそんなによいプログラム言語なのでしょうか。ほかのプログラム言語、例えばCやC++やJavaやC#よりも、優れている点が本当にあるのでしょうか? 小さな事例でよいので、何か1つ教えてください。

力と技のVBプログラマV3号 より

Exitステートメント 対 breakステートメント

 ご希望とあれば、CやC++やJavaやC#よりも、Visual Basicの方がちょっとさえていると私が思っている事例を1つ紹介しましょう。もしかしたら、人によっては、こんなものはさえてなどいないと思うかもしれませんが、私はけっこう気に入っています。また、正しさよりも、こんな事例もあるのだと思ってVBプログラマが少しは勇気とやる気を持てることの方が、この連載では重要です。

 さて、その事例とは、ループなどから途中で脱出するために使用するExitステートメントです。これと、C#にあるbreakステートメントを比較してみましょう。C/C++/Javaにも、C#とほぼ同じものが存在しますが、ここでは代表としてC#を使います。

 Visual BasicのExitステートメントは、ループなどのブロックから脱出する機能を実現する構文です。いわゆる「飼いならされたgoto文」と呼ばれるものに当たります。書式は、Exitキーワードの後に、脱出する対象の名前を書き込みます。例えば、Doループから脱出する場合はExit Do、Forループから脱出する場合はExit For、関数(Function)から脱出する場合はExit Functionです。これは、VBプログラマから見れば当たり前のことに思えるかもしれませんが、必ずしもほかのプログラム言語でも当たり前というわけではないのです。C#のbreakステートメントは、ループと、Visual BasicのSelect Caseステートメントに対応するswitchステートメントから脱出する役割を持っていますが、脱出対象を指定するキーワードを付けません。この違いがどういう問題を引き起こすか、実例を簡単なサンプル・コードで見てみましょう。

Visual Basic .NETでExitステートメントを使う

 Exitステートメントを使った簡単なメソッドを作成してみました。内容は簡単で、整数の変数を1つ宣言し、その値を増やしながら繰り返します。変数の値が5になったら「count is 5」というメッセージを出力し、10になったらExitステートメントでループを抜け出します。そして、最後に変数の値を出力しています。このような処理を行う場合、回数が決まっていればForステートメントを使った方がよいのですが、これはあくまで説明の都合上のサンプル・コードなので、このような構成になっています。

Sub Sample1()
  Dim count As Integer = 0
  Do
    count += 1

    If count = 5 Then
      Console.WriteLine("count is 5")
    ElseIf count = 10 Then
      Exit Do
    End If
  Loop
  Console.WriteLine(count)
End Sub

 さて、ここでIfステートメントが、同じパターンで繰り返されていることが目に付くと思います。同じ変数と定数の一致判定が2回繰り返されています。これは、Select Caseステートメントを使って一体化することができます。一体化を実行するのは簡単で、If…ElseIf…End Ifの各行を、Select Case…Case…End Selectに書き換えればよいわけです。書き換えた結果は以下のとおりです。

Sub Sample2()
  Dim count As Integer = 0
  Do
    count += 1

    Select Case count
      Case 5
        Console.WriteLine("count is 5")
      Case 10
        Exit Do
    End Select
  Loop
  Console.WriteLine(count)
End Sub

 Visual Studio .NET 2003にて、コンソール・アプリケーションのプロジェクトを作成して、この2つのメソッドを書き込んだうえで、Mainメソッドに以下のようにメソッドを呼び出すコードを追加してみましょう(コンソール・アプリケーションについては、別の機会で取り上げて、その効能と愉悦について一説を披露したいと思います。ここでは単純にコードがシンプルになるからコンソール・アプリケーションにしたということで、話を進めます)。

Sub Main()
  Sample1()
  Sample2()
End Sub

 これを実行すると以下のような結果が出力されます。

count is 5
10
count is 5
10

 2つのメソッドは完全に同じ結果を出力していて、何の問題もありません。

 VBプログラマにとって当たり前の書き換えですが、実はC#プログラマ(そして、C/C++/Javaプログラマ)にとって、これは当たり前の書き換えにならないのです。

C#で同じ書き換えをやってみよう

 VBプログラマであれば、以下のC#のサンプル・コードは見ても理解できないかもしれませんが、Visual Basic .NETのサンプル・コードとほぼ1対1で対応しているので、何となく分かると思います。ここではifがIfに、else ifがElseIfに、breakがExit Doに対応していると分かれば十分です。

public static void Sample1() {
  int count = 0;
  for (;;) {
    count += 1;

    if ( count == 5 ) {
      Console.WriteLine("count is 5");
    } else if ( count == 10 ) {
      break;
    }
  }
  Console.WriteLine(count);
}

 さて、これにVisual Basic .NETのサンプル・コードに行ったのと同様な書き換えを行ってみましょう。

public static void Sample2() {
  int count = 0;
  for (;;) {
    count += 1;

    switch ( count ) {
      case 5:
        Console.WriteLine("count is 5");
        break;
      case 10:
        break;
        break;
    }
  }
  Console.WriteLine(count);
}

 Visual Stduio .NET 2003にて、コンソール・アプリケーションのプロジェクトを作成して、この2つのメソッドを書き込んだうえで、Mainメソッドに以下のようにメソッドを呼び出すコードを追加してみましょう。

static void Main() {
  Sample1();
  Sample2();
}

 さて、書き換えられたソース・コードは、switchがSelect Caseに、caseがCaseに対応すると分かれば、だいたいどこをどう書き換えたか分かるでしょう。しかし、その知識だけでは理解できない奇妙な行が2つほど見られると思います。それは、「case 5:」の2行先と、「case 10:」の2行先にある2つのbreakステートメントです。この2行に対応する行は、Visual Basic .NETのソースには存在しません。

 これらのbreakステートメントの役割は、case以下の行の実行を終了し、switchステートメントから脱出させることにあります。Visual BasicのSelect Caseステートメントの場合、次のCaseキーワードが出現したら、そこで処理は終わります。ですから、特にSelect Caseステートメント内の処理を終了させ、脱出させるステートメントを記述する必要はありません。そのため、Visual Basic .NET版のサンプル・コードにはそのようなステートメントを記述していません。しかし、C#ではそれが必要とされます。もし、このbreakを省略すると、コンパイル・エラーになります。

 余談ですが、もっとややこしいのは、Cなどの場合です。Cの場合はbreakステートメントを書き忘れてもコンパイル・エラーにならず、次のcase以下の行の実行を続けます。そのような動作を前提にして、コーディングを行う場合もありますが、たいていはbreakの書き忘れというバグの温床となります。switch文でのbreakの書き忘れは、Cの定番コーディング・ミスの1つなのですが、Visual BasicのSelect Caseステートメントで、それはあり得ません。少なくとも、この1点に関していえば、Cで落とし穴になる問題が、Visual Basicには存在しないといってよいと思います。とはいえ、今回の主テーマは、この点でVisual Basicの方がさえていると主張することではありません。

脱出対象の指定が必須なVisual BasicのExitステートメント

 さて、C#では通常、caseを記述したら、次のcaseの手前あるいはswitchステートメントが終了する手前にbreakステートメントを記述します(そうではない書き方もあります)。その通例に従って、サンプル・コードでは、case 5:とcase 10:の終わりにそれぞれbreakステートメントを入れてあります。すると、妙なことが起こります。case 10:の次の行には、書き換え前のソース・コードからコピーしてきたbreakステートメントが入ります。そして、その次の行には、switchステートメントから脱出するbreakステートメントを記述することになります。つまり、まったく同じbreakステートメントが2行続くことになってしまいます。

 これを実行すると以下のような結果が出力され、その後、永遠にプログラムは終了しません。つまり、書き換え後のプログラムは、書き換え前と同じ結果を出力してくれません。

count is 5
10
count is 5

 なぜ、このプログラムが永遠に終了しないのでしょうか。その理由は簡単です。書き換え前のサンプル・コードに含まれるbreakステートメントはループから脱出するという機能を与えられていました。しかし、switchステートメントの中に移動されたbreakステートメントは、それが持つ機能(ループとswitchステートメントからの脱出)に素直に従い、switchステートメントからの脱出として機能し、ループからの脱出としては機能しなくなっています。そもそも、case 10:という行の後に、breakが2行連続してしまっているのが「おかしい兆候」を示しているわけです。この2行というのは、本来ループ脱出用であったbreakステートメントと、switchステートメントからの脱出用のbreakステートメントであったものです。しかし、どちらもswitchステートメントからの脱出用のbreakステートメントとして機能してしまいます。

 このように「おかしい兆候」をすぐに発見できればよいのですが、実際にコードを書き換えている場合は見逃すことも多いのです。例えば、複雑なコードの塊をswitchステートメントの中に移動させたりすると、そのような兆候に気付くことなく、誤った書き換えを行ってしまうことがあります。

 しかし、このような問題は、Visual Basicではあまり起きません。なぜなら、Visual BasicのExitステートメントは、自分が脱出する対象を明示して記述するからです。上記の例なら、Doループから脱出するExitステートメントはExit Doと記述しているため、それがSelect Caseステートメントの内部に移動したとしても、Doループから脱出するという機能に何ら変わりはないのです。もちろん、Doループが2重3重になっている場合、Exit Doの場所を変えると脱出する対象のループが変わってしまう可能性はありますが、過去の経験からいうと、そういう問題にはあまり遭遇しません。やはり、ループ脱出のbreakステートメントをswitch文の中に移動させてしまうケースが多いように感じます。

 余談ですが、ループ内部に記述されたswitchステートメントからそのループを脱出する場合、C/C++/C#では主にgotoステートメントを使うことになります。Javaではループに識別子を付けて、その識別子をbreakキーワードの後に記述して行うことができます。しかし、いずれの方法も、あらかじめ分かっていて、そのようなコーディングを行わないと対処できないものです。例えば、ループに識別子を付けるというのは強制ではありません。たいていはなくても問題ないので付けません。付いていないコードに、それと気付かないで誤った修正を加えれば、トラブルのわなに落ちてしまいます。しかし、Visual BasicのExitステートメントの場合、その後に記述する脱出する対象の指定は必須であるため、対象があいまいなソース・コードになることはありません。

 ところで、対象の指定が必須であるということは、それだけソース・コードに入力する文字数が増えるということを意味します。C/C++/Java/C#などは、記号類を活用して比較的文字数の少ないソース・コードを実現していますが、Visual Basicはそれとは異なる方向性を持っているといえます。もちろん、ただ冗長に文字を増やしているだけなら手間が増えるだけで意味がありません。しかし、Visual Basicの場合は、プログラマの意図を明確化するという目的で文字量が増えているという側面があって、そこは筆者としてはけっこう好感を持っている特徴です。

当たり前の構文にこだわる理由

 さて、今回説明したような、ほかの言語との文法の比較であるとか、常識的な構文にいちいちこだわることは、あまり意味がないと思う読者もいるかもしれません。VBプログラマがVBプログラマとして仕事をするうえで、こういった知識が必ずしも要求されるわけではないことは筆者もよく承知しています。しかし、この連載では、時々こういう話題も取り上げていこうと思っています。もちろん、いつもこういう話題ではおもしろくありませんから、あくまで時々です。

 なぜ、取り上げたいと思うのか。それは、志の問題です。目の前の仕事に必要な知識しか持たないとすれば、それだけのプログラマで終わってしまうでしょう。けれども、当面は仕事に必要がない知識であっても、「もっとよくVBのことを知ろう」「ほかの言語と違うVBの特徴を知ろう」という高い志を持ち続ければ、思いもしないところでそれが新たな可能性を切り開いてくれるかもしれません。

 そしてVisual Basic .NETは、さらに上を志すプログラマが投資するに値する中身のあるプログラム言語だと思います。向上心を持って努力すれば、今の何倍にも輝くことができるでしょう。その輝きが人々に認められれば、「VBプログラマなら素晴らしい仕事をしてくれる」と考える人たちが増え、VBベースでの開発案件の増加につながるでしょう。これが最も望ましいゴールだと思います。

 頑張れVBプログラマ、君たちが使うVisual Basic .NETは取り組む価値のある可能性に満ちたプログラム言語だ!End of Article

インデックス・ページヘ  「熱血VBプログラマ応援団」


Insider.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用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

業務アプリInsider 記事ランキング

本日 月間
ソリューションFLASH