Visual Basic 初級講座
VB.NET 2002 対応 VB.NET 2003 対応 VB2005 対応

 

Visual Basic 中学校 > 初級講座 >

第11回 メソッドを作る

今回はメソッドを作る方法とその利用の仕方を説明します。今回説明するのはメソッドの作り方の基本部分ですが、それだけでも多くのメソッドを作れるようになります。

この回の要約

・メソッドはプログラムを整理するために作る

・種類別のメソッドの作り方

・メソッドを利用する例

 

1.はじめに

 

この初級講座では、今までほとんどのプログラムをイベントプロシージャに書いてきました。しかし、実際にはイベントプロシージャしか使わないプログラムはごく限られた機能を持つものだけです。

VBではイベントプロシージャ以外のプロシージャも自由に作ることができます。このようなプロシージャをメソッドまたは関数とよんだりします。メソッドや関数というとVBにあらかじめ用意されているものを使うという印象しかない方もいらっしゃるかもしれませんが、このようにVBでは自分でメソッドを作って自分で呼び出すこともできるのです。

これから説明しますが、今回は下のサンプルのようなプログラムの仕組みと作り方を説明します。

VB.NET2002対応 VB.NET2003対応 VB2005対応 

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

    WriteLog("C:\Test.txt", "自作メソッドのテスト")

End Sub

Private Sub WriteLog(ByVal FileName As String, ByVal Description As String)

    Dim Writer As New IO.StreamWriter(FileName, True)

    Writer.WriteLine(Now.ToString("yyyy/MM/dd hh:mm:ss") & " " & Description)

    Writer.Close()

End Sub

■リスト1:ログファイルに書き込むメソッド

このプログラムではWriterLogというメソッドを自分でプログラムしています。このメソッドはファイル名と文字列を渡すと、そのファイルに日付と時間とともに文字列を書き込みます。Button1Clickイベントからはこのメソッドを呼び出しています。

 

2.なぜメソッドを作るのか?

 

メソッドの作り方を説明する前に、メソッドを作る利点を説明します。

なお、 一口に「メソッドを作る」といっても、普通の画面を持ったプログラム内にメソッドを作る場合と、クラスを自作している場合にメソッドを作る場合とでは作り方は同じでも意味合いが違います。ここでは、普通の画面を持ったプログラム内にメソッドを作る場合を説明します。

メソッドの利点はプログラムを整理できることです。整理されたプログラムは、機能追加や修正が簡単です。また、他の人がプログラムを見た場合にもプログラムを容易に理解することができます。

では、どのように整理できるのかもう少し細かく見てみましょう。

・同じような処理を一箇所にまとめられる。

プログラムのいたるところに似たような処理を書くのではなく、その処理をメソッドとして独立させ、各所からはそのメソッドを呼び出すようにするとプログラムはぐっと整理されます。

・面倒な処理を一箇所にまとめられる。

複雑な手順が必要な処理はメソッドとして独立させると、あとはそのメソッドを呼び出せばいいだけなのでとても楽になります。

・意味のある処理を一箇所にまとめられる。

プログラム中で一回しか書かないコードでも独立させてメソッドにしたほうが分かりやすい場合もあります。

たとえば、OKボタンをクリックしたらファイルにデータを書き込むプログラムを考えた場合、ファイルにデータを書き込む部分をOKボタンのClickイベントに書かないで、メソッドとして独立させます。

プログラマはOKボタンのことを気にすることなく、データ書き込み処理をプログラムすることができますし、OKボタンをクリックしたときに別の処理も行おうとした場合、やはりデータ書き込み処理を気にすることなくプログラムを変更できます。

 

3.メソッドの作り方

 

それでは、いよいよメソッドの作り方を説明します。メソッドを作るのは覚えてしまえばいたって簡単です。

細かい説明をする前に作り方の要点を表にまとめておきます。

引数がなく、値を返さないメソッド Private Sub methodname()

End Sub

引数があり、値を返さないメソッド Private Sub methodname(arg1 As type1)

End Sub

引数がなく、値を返すメソッド Private Function methodname() As type1

    Return value1

End Function

引数があり、値を返すメソッド Private Function methodname(arg1 As type1) As type2

    Return value1

End Function

■表1:まとめ

 

4.値を返さないメソッドの作り方

 

もっともシンプルなのは、引数がなく値を返さないメソッドです。

このようなメソッドは単純に処理の一部を独立させるために使います。処理の一部を独立させることにより、同じ処理をあちこちに書く必要がなくなりますし、プログラムがわかりやすくなります。

引数が必要ない場合、このメソッドの宣言の構文は次の通りです。(引数がある場合は少し後で説明します。)

VB6対応 VB.NET2002対応 VB.NET2003対応 VB2005対応 

Private Sub methodname()

End Sub

■構文1:値を返さないメソッド

methodnameの部分には、メソッドの名前を設定してください。そして、メソッドの内容はイベントプロシージャ同様、Private SubEnd Sub の間に記述してください。

Subステートメント(読み方:Sub = サブ)は値を返さないメソッドを作ります。その前に書かれているPrivate(読み方:Private = プライベート)はこのメソッドがどのようにアクセスできるかを指定しているのですが、今はあまり気にしないでおきます。

これを使って現在時刻を表示するメソッドShowNowを作るとすると次のようになります。ShowNowの部分は自分で好きな名前をつけて構いません。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Private Sub ShowNow()

    MsgBox(Now)

End Sub

■リスト2:現在の時刻を表示するメソッド

この例ではメソッドの内容は1行しかありませんので実用的ではありません。あくまで説明用と思ってください。というのもこのタイプのメソッドは複雑なプログラムでこそありがたみがでてくるものなのです。そして今複雑なプログラムを紹介すると本題がわかりにくくなっていしまいます。

このメソッドを呼び出すには呼び出したい位置に次のように記述するだけです。

ShowNow

また、Callステートメントを使って次のように記述することもできます。

Call ShowNow

なお、Now(読み方:Now = ナウ)は現在の日付と時間を表すプロパティです。DateAndTimeクラス(読み方:DateAndTime = デイトアンドタイム)のプロパティなのですが、このクラスはStrings等と同様VBにはじめから組み込まれている省略可能な特別なクラスなのでDateAndTime.Nowではなく、ただ単にNowと書くのが一般的です。

 

同じように引数のあるメソッドを作る場合は、かっこの中に引数の名前と型を順番に指定します。

はじめにでてきた、テキストファイルに日付と時刻をつけて文字を書き込むWriteLogメソッドは次のようになります。 このメソッドはログファイルを作成するときにそのまま使えます。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Private Sub WriteLog(ByVal FileName As String, ByVal Description As String)

    Dim Writer As New IO.StreamWriter(FileName, True)

    Writer.WriteLine(Now.ToString("yyyy/MM/dd hh:mm:ss") & " " & Description)

    Writer.Close()

End Sub

■リスト3:ログをファイルに書き込むメソッド

このメソッドはFileNameDescriptionの2つの引数を取ります。 名前から想像が付くと思いますが、FileNameで指定したファイルにDescriptionで指定した文字列を書き込みます。このように引数や変数の名前はその引数や変数の機能が想像できるようなものにしてください。

引数の宣言の前にあるキーワードByVal(読み方:ByVal = バイバル)はこのメソッドの中でこの引数の値を変更しても呼び出し元には影響しないことを表していますが、特に気にする必要はありません。省略してもVBが自動的につけてくれるのでキーボードから打ち込む必要すらありません。

引数として宣言したFileNameDescriptionはこのプロシージャ(つまりWriteLogメソッド)の中では通常の変数と同じように扱うことができます。この2つの引数に実際にどのような値が入ってくるかは呼び出し側が決めることです。

このメソッドを呼び出すには呼び出したい位置に次のように記述します。

WriteLog("C:\Test.txt", "メソッドのテスト")

先頭にCallステートメントをつけて次のように書くこともできます。

Call WriteLog("C:\Test.txt", "メソッドのテスト")

そのほか、プログラム中にファイルへの書き込みに関するコードがありますが、これらについては今回は説明しません。

 

5.値を返すメソッドの作り方

 

値を返すメソッドは別名「関数」とも呼ばれます。このようなメソッドは処理の一部を独立させたり、複雑な処理の補助に使います。プログラムが巨大で複雑になるほど関数の必要性は増します。一定以上の規模のプログラムでは関数を作らないなどということは考えられません。

引数が必要ない場合、このメソッドの宣言の構文は次の通りです。(引数がある場合は少し後で説明します。)

VB.NET2002対応 VB.NET2003対応 VB2005対応

Private Function methodname() As typename

Return value

End Sub

■構文2:値を返すメソッド。別名「関数」。

値を返すメソッドを宣言するにはSubではなく、Functionステートメント(読み方:Function = ファンクション)を使用します。

返す値の型はメソッドの宣言とともに指定する必要があります。上記の雛形(ひながた)ではAs typenameと書いておきましたが、このtypenameのところに返す値の型を指定します。

またメソッド内で返す値を指示するにはReturnステートメント(読み方:Return = リターン)を使用します。 上記の雛形でvalueのところに返したい値を指定してください。なお、Returnが実行されるとその下にあるコードは一切実行されないので注意してください。

たとえば、次のメソッドは日本の元号で年を返します。たとえば、2005年にこのメソッドを呼び出すと、17を返します。2005年は平成17年だからです。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Private Function GetJapaneseYear() As Integer

    Dim Japan As New Globalization.JapaneseCalendar

    Dim JapaneseYear As Integer

    JapaneseYear = Japan.GetYear(Now)

    Return JapaneseYear

End Function

■リスト4:現在の年を和暦で返す。

この例も内部のコードの説明は割愛します。

このメソッドは通常のメソッドと同じように呼び出したり、値を受け取ったりできます。

なお、あまり使いませんが、Returnステートメントを使わなくても「メソッド名 = 戻り値」という記述で値を返すこともできます。

上の例で言うと Return JapaneseYearではなく、GetJapaneseYear = JapaneseYear という記述も可能ということです。

この2つは書き方が違うだけでなく機能も少し違います。Returnが実行された場合、その行より下にプログラムがあってもすべて無視されますが、「メソッド名 = 戻り値」という書き方の場合、その行より下にあるプログラムも実行されます。

 

最後に値を返すメソッドで引数が必要な場合について説明します。といってもここまでの説明ですでに十分だと思いますので、このパターンは例だけ示しておきます。次のメソッドIsEvenは引数が偶数の場合True、そうでない場合Falseを返します。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Private Function IsEven(ByVal Value As Integer) As Boolean

    Return Value Mod 2 = 0

End Function

■リスト5:数値が偶数であるか判断する

これも内部のコードの説明は省略します。 = 演算子については後日説明する予定です。要は「2で割ったあまりが0だったら偶数」という算数の基本的なことを行っているに過ぎません。

このメソッドは通常のメソッドと同じように呼び出したり、値を受け取ったりできます。

 

6.高度なメソッドについて

 

以上、基本的なメソッドの作り方を説明しましたが、メソッドにはもっとこったことができます。たとえば、引数の数を可変にすることができます。また、引数を省略可能にすることもできます。それから名前は同じだけれども引数が異なるメソッドを作ることもできます。

しかし、これらの高度なメソッドの作り方は以下で簡単に紹介するにとどめ、詳細はまたの機会に説明します。

 

メソッド自体に組み込めるさらなる高度な機能

・可変引数

キーワードParamArrayを使用すると、引数の数を固定ではなく可変にできる。

・省略可能引数

キーワードOptionalを使用すると、省略可能な引数とその既定値を設定できる。

・多重定義

キーワードOverloadsを使用すると、同じ名前で異なる引数のメソッドを複数作れる。

・上書き

キーワードOverridesまたはShadowsを使用すると、あらかじめ用意されているメソッドの内容を変更できる。

・引数の値を変更する

キーワードByRefを使用すると、メソッドの中で引数の値を変更して呼び出し元に反映することができる。

 

7.事例

 

最後に、メソッドを作る利点を感じ取っていただくためにちょっと複雑なプログラムを用意してみました。

このプログラムはキーボードの画面上のボタンをクリックすることにより円を左右に移動させるプログラムです。

円はグラデーションで描画します。背景色は黒です。

■画像1

まずフォームに次の表のように2つのボタンを配置してください。

コントロール プロパティ
btnLeft (Button) Text <
btnRight (Button) Text >

■表2

さて、イベントプロシージャだけでこのプログラムを書くと次のようになります。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim CircleX As Integer = 150
Dim CircleY As Integer = 100

Private Sub btnLeft_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLeft.Click

    CircleX -= 5

    Dim g As Graphics = Me.CreateGraphics

    Dim b As New Drawing2D.LinearGradientBrush(New Point(CircleX, CircleY), New Point(CircleX + 50, CircleY + 50), Color.Yellow, Color.LightBlue)

    g.Clear(Color.Black)
    g.FillEllipse(b, CircleX, CircleY, 50, 50)

End Sub

Private Sub btnRight_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRight.Click

    CircleX += 5

    Dim g As Graphics = Me.CreateGraphics

    Dim b As New Drawing2D.LinearGradientBrush(New Point(CircleX, CircleY), New Point(CircleX + 50, CircleY + 50), Color.Yellow, Color.LightBlue)

    g.Clear(Color.Black)
    g.FillEllipse(b, CircleX, CircleY, 50, 50)

End Sub

■リスト6:メソッド化されていない煩雑なコード

これでも問題なく動くのですが、無駄がいっぱいです。薄いピンク色で塗ってある部分はまったく同じ処理なので2箇所に分断されているのはよくありません。ここでメソッドの出番です。メソッドを使って円を描画する部分を一箇所にまとめるてみましょう。次のようになります。新しいメソッドにはDrawCircleという名前をつけました。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim CircleX As Integer = 150
Dim CircleY As Integer = 100

Private Sub btnLeft_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLeft.Click

    CircleX -= 5

    DrawCircle(CircleX, CircleY)

End Sub

Private Sub btnRight_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRight.Click

    CircleX += 5

    DrawCircle(CircleX, CircleY)

End Sub

Private Sub DrawCircle(ByVal X As Integer, ByVal Y As Integer)

    Dim g As Graphics = Me.CreateGraphics

    Dim b As New Drawing2D.LinearGradientBrush(New Point(X, Y), New Point(X + 50, Y + 50), Color.Yellow, Color.LightBlue)

    g.Clear(Color.Black)
    g.FillEllipse(b, X, Y, 50, 50)

End Sub

■リスト7:メソッド化されて整理されているコード

これですっきりしました。機能は修正前と全く一緒ですが、円を描く部分が一箇所にまとまっているので色や大きさを変えるのが簡単ですね。円の色や大きさを変える手間が修正前のプログラムの半分になっている点に注意してください。これだけの規模のプログラムでも手間が軽減できるのですから、ある程度まとまったプログラムで以下にメソッド化(関数化)が物を言うかわかります。

さらに、左右だけでなく上下にも移動させたいと思ってもとても簡単にできてしまいますね。

ところで、DrawCircleメソッドは座標XYを引数に取るように作りましたが、構文上は引数としてXYが必要なわけではありません。XYは引数にしなくても共通変数のCircleXCircleYを参照すればよいのですから問題ないはずです。

しかし、この場合座標はあくまで引数にするのが良いです。ここのところあまり市販の本や雑誌には書いていませんが、まともなプログラマならみんなわかっていることなのでよく聞いてください。以前にも説明したように、メソッドとは正しい引数を渡せば予想通りの動作をしてくれるものなのです。メソッドを呼び出す側ではメソッドの中身がどうなっているかは気にしなくて良いのです。

■画像2:良いメソッド

しかし、ここでDrawCircleメソッドが共通とはいえメソッドにとっては外部の変数CircleXCircleYを参照するとなると、メソッドの呼び出し側では、メソッドの呼び出し方の他に、共通変数の値も管理しないとメソッドが正しい動作をしてくれないということになってしまいます。これではメソッドの管理が煩雑になり、プログラムが複雑になっていくにつれ思わぬバグの原因となってしまうのです。

■画像3:良くないメソッド

ここで混乱を招いてしまうかもしれませんがもう1つ補足しておきます。メソッドの中には多機能を前提に設計されているメソッドもあります。 これは初めの方に紹介した「面倒な処理を一箇所にまとめられる。」という利点を持つメソッドのことです。そのようなメソッドの場合は外部の変数などに依存しても問題ありません。

どのメソッドが多機能なメソッドでどのメソッドが通常のメソッドなのかの切り分けの大部分は経験によるしかないと思いますが、私の経験では自作するメソッドの9割は通常のメソッド、つまり外部の変数に依存しないメソッドとなります。