Visual Basic 中学校 投稿プログラム |
健康診断 BMI測定
投稿者:root.tools.programさん
「健康診断 BMI測定」は身長と体重を入力するとBMI(肥満度)と理想体重を計算して表示するプログラムです。ただし、今回投稿していただいたプログラムはエラー処理ができていません。博士と一緒に基本的な計算プログラムの中身を勉強して、さらにエラー処理についても勉強してしまいましょう。
ダウンロード
BMI.lzh(33.6KB)
VB2005で作成されたソースコード。プログラマ向けです。 ソースコードを編集するためにはVB2005またはVB2008が必要です。Express Editionでも大丈夫です。 VB2008で作業すると、最初だけ変換ウィザードが実行されます。 ソースコードの改変・公開・配布は自由です。 |
V太:博士!新作プログラムが到着しました!
■画像1
B子:BMI測定ね…。
BMIって何?
肥満度のことよ。体重だけでくらべたら身長の高い人も低い人も同じ太り具合ってなっちゃうでしょ。だから体重と身長をもとに「肥満度」を計算しましょうっていう数字よ。具体的には体重÷(身長×身長)を計算して22なら理想的、25以上は肥満という感じね。
ほぉほぉ。よく知ってるなぁ。さっそく入れてみようか。ぼくは体重44Kgで、身長150cmだから、44と150を入力して、「計算する」ボタンを押すと…
おわー!なんじゃこりゃ!BMIは0で、理想体重は495?! ありえないよ〜!
■画像2:理想体重が495!?
あのねぇ。身長は m で入れるのよ。V太は150cmだから、1.5と入れなさい。そうすると、ホラ、BMIは19.5で理想体重は49.5みたいね。ちょっとやせているということになるのかしら?BMIの参考表によるとやせ気味くらいね。
あーびっくりした。
博士:さて、それじゃプログラムを見てみようかの。今回は機能がシンプルなのでこれが全プログラムじゃ。
Private Sub ComputeButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Dim X As Decimal Dim Y As Decimal Dim BMI As Decimal Dim Ideal As Decimal X = WeightTextBox.Text ''体重 Y = HeightTextBox.Text ''身長 BMI = X / (Y * Y) Ideal = 22 * Y * Y BMITextBox.Text = BMI IdealTextBox.Text = Ideal End Sub |
■リスト1:全プログラム
おぉー!なんだかわかりやすいです!
変数BMIがそのままBMIで、Idealは理想体重ね。計算式さえしっていれば組めるプログラムだわ。
じゃあ、少し時間をとってよいから、このプログラムのどこにバグがあるか考えてみるのじゃ。
むむむー………
わかったわ!2つ見つけた!
えっ!どこ?
たとえば、X = WeightTextBox.Text としている個所があるわよね。XはDecimalで宣言しているから数値でしょ。だから体重のテキストボックスに数値として解釈できない文字を入力すると型変換エラーになるはずだわ。
なるほど。たとえば体重に「あいうえお」とかを入力して「計算する」ボタンを押すとエラーになるということか。身長も同じだね。
■画像3
もう1つのバグはオーバーフローね。Decimalはかなりおおきな桁数の数字を記憶できる型だけどそれでも限界はあるわ。体重や身長のテキストボックスにDecimalが対応できないくらい9をいっぱい入力して「計算する」ボタンを押すと、オーバーフローになるわ。
ふむ。正解じゃ。つけくわえると、型変換エラーの方は何も入力しないで「計算する」ボタンを押しても発生するぞ。だからプログラム開始直後にいきなり「計算する」とエラーになるのじゃ。
なるほどです。
いや、「なるほどです。」で済ませてはいかん。それではこのプログラムをどう修正すればよいのかを考えるのじゃ!
はーい。
できました!こんなのどうですか?
Dim X As Decimal Dim Y As Decimal Dim BMI As Decimal Dim Ideal As Decimal Try X = WeightTextBox.Text ''体重 Y = HeightTextBox.Text ''身長 BMI = X / (Y * Y) Ideal = 22 * Y * Y BMITextBox.Text = BMI IdealTextBox.Text = Ideal Catch ex As Exception 'エラーが発生したら結果を消してメッセージ表示 BMITextBox.Text = "" IdealTextBox.Text = "" MsgBox("体重・身長を正しく設定してください。", MsgBoxStyle.Information) End Try |
■リスト2:V太修正版
Tryブロックを使ってエラーが起こったらとにかくメッセージを表示するというわけじゃな。うむ、いいのではないかな?
やったー!
ただ、今回は小さいプログラムだからこれでもよいが、常にTry〜End Tryでくくって大雑把にメッセージを表示すればいいというものでもないぞ。もうひと工夫欲しいのぉ。
それじゃ、こんなのはどうかしら?
Dim X As Decimal Dim Y As Decimal Dim BMI As Decimal Dim Ideal As Decimal '体重をXにセットする。失敗する場合はメッセージを表示して処理を中断。 If Decimal.TryParse(WeightTextBox.Text, X) = False Then WeightTextBox.Focus() WeightTextBox.SelectAll() MsgBox("体重を正しく入力してください。", MsgBoxStyle.Information) Return End If '身長をYにセットする。失敗する場合はメッセージを表示して処理を中断。 If Decimal.TryParse(HeightTextBox.Text, Y) = False Then HeightTextBox.Focus() HeightTextBox.SelectAll() MsgBox("身長を正しく入力してください。", MsgBoxStyle.Information) Return End If BMI = X / (Y * Y) Ideal = 22 * Y * Y BMITextBox.Text = BMI IdealTextBox.Text = Ideal |
■リスト3:B子修正版
ほー。これは丁寧じゃ。体重、身長に入力した値を1つずつチェックしていくわけじゃな。
チェックっていったって、Decimal型に変換できるかなんてどうやって調べたらいいんですか?どうなってるんです、このプログラム?ぼくにはよくわかりません。
ふふーん。私は常に最新の情報を集めているのよ。最近のVBには各型にTryParseというメソッドが追加されていて、このメソッドを使うとその型に変換できるか簡単に調べられるのよ。たとえば、Integer.ParseならInteger型に変換できるか調べられるし、Decimal.TryParseならDecimal型に変換できるかわかるわ。
※B子ちゃんは大雑把に「最近のVB」と言っていますが、正確にはVB2005から使用可能です。
いやいや。よくわからないよ!だったら、Decimal.TryParse(WeightTextBox.Text)でいいはずじゃないのか?なんで第2引数にXを指定しているんだ?
ははーん。それがこのメソッドの面白いところでね。変換可能な場合は、変換結果を第2引数の変数にセットしてくれるのよ。だから、TryParseがTrueだった場合にそのあとでわざわざ X = WeightTextBox.Text なんて書かなくてももうXには体重が入っているってわけよ…。
むむむー!いつの間にかそんなメソッドが追加されていたのか〜!
いつの間にって3年以上前なんだけど…。
だけどB子。これってオーバーフローも防げるの?
もちろん、文字だろうが、大きすぎる数字だろうがとにかくDecimalに記録できないものはすべてチェックできるわ。
でもさぁ、たしかに身長に「999999999999999999999999999999」って入力するとB子のチェックにひっかかってメッセージが表示されるんだけど、「99999999999999」って入力するとオーバーフローのエラーになるよ。
あらら…。そうか、身長だけ見ていればオーバーフローしていないけど、後で Y * Y を計算するときにかけ算の答えがオーバーフローするのね…。しかたない、ここだけV太と同じようにTry 〜 End Tryを入れるか…。
(V太も随分いじわるな値でテストするのぉ)
うーん。それよりもさぁ、考えてみたらそもそも体重と身長がDecimalである必要はないんじゃないの?そんなすごい体重と身長の人いないよね。
そうか!XとYの型をShortに変更してDecimal.TryParseの代わりにShort.TryParseにすれば大きな桁数が入力できなくなって解決だ!Short同士なら最大値でかけ算してもDecimalに代入できるはずだよね。
※Shortの最大値は32767です。
Dim X As Short Dim Y As Short Dim BMI As Decimal Dim Ideal As Decimal '体重をXにセットする。失敗する場合はメッセージを表示して処理を中断。 If Short.TryParse(WeightTextBox.Text, X) = False Then WeightTextBox.Focus() WeightTextBox.SelectAll() MsgBox("体重を正しく入力してください。", MsgBoxStyle.Information) Return End If '身長をYにセットする。失敗する場合はメッセージを表示して処理を中断。 If Short.TryParse(HeightTextBox.Text, Y) = False Then HeightTextBox.Focus() HeightTextBox.SelectAll() MsgBox("身長を正しく入力してください。", MsgBoxStyle.Information) Return End If BMI = X / Y * Y Ideal = 22 * Y * Y BMITextBox.Text = BMI IdealTextBox.Text = Ideal |
■リスト4:V太の問題のあるプログラム
おっと。これはいろいろまずいのぉ!確かにかけ算してもDecimalはオーバーせんが、このプログラムだとかけ算の結果Shortをオーバーした時点でオーバーフローになる。
えっ!だって、BMIとIdealはDecimalのままですよ。
ふむ。VBはShort同士をかけ算させると結果もShortだと勝手に予測してしまうのじゃ。だから、この例のように最終的にはDecimal型の変数にセットするシーンでも内部で一時的にShortで計算しようとする。そうすると、Shortに記録できない大きな数字はやはりオーバーフローになってしまうのじゃ。
式の中で、BMI = X / Y * CDec(Y)のようにしてを項目を1つでもDecimal型に変換すると、VBはShortとDecimalのかけ算と判断して内部的にもDecimalで計算してくれる。じゃが、これはわかりにくいし、このプログラムはCDecだけじゃかわせん重大な問題があるぞ。
むむ!?
V太。あのねぇ。V太のプログラムじゃ小数が入力できないのよ。さっき身長を1.5って入力したでしょ?でもShortは整数しか入力できないから1.5は認識されなくなっちゃうわけ。
そういうことじゃ。
いいこと思いついたわ!テキストボックスのMaxLengthプロパティを3か4にしたらいいじゃないかしら?こうすればテキストボックスには体重も身長も3桁か4桁しか入力できないからDecimalがオーバーフローすることはありえないわ。私の最初のプログラムがそのまま使えるわね。
おー!これでわかりにくいCDecもいらないし、Shortもいらないです!
素晴らしい!なによりもとても簡単にできるという点がいいのぉ。
このように値のチェックやエラー処理にはいろいろなやり方があるのじゃ。問題の1つはどのようにチェックするかということで、V太君はTryブロックを使い、B子ちゃんはIf文でTryParseメソッドを使って判断しておった。
もう1つの問題はチェックした結果異常があった場合どうするかということで、V太君は結果をクリアしてメッセージを表示しておったし、B子ちゃんは間違いのあるテキストボックスにフォーカスを移動してメッセージを表示しておった。
どのような方法でも目的は達成できるかもしれんが、ユーザーの使い心地やプログラムの面倒さが大きく違ってくる。規模の大きいプログラムを作るときははじめにこのことをよく考えておかないと後で痛い目を見るのじゃ。みんなも甘く見ないように!
今日はもうちょっと勉強したい気分です。この他のエラー処理にはどのようなものがありますか?
(V太が真面目な的を得た質問をしているわ!)
それでは、エラー処理に関連した手法をあと2つほど紹介しようかの。
まず、メッセージを表示する代わりに画面にエラーアイコンを表示してユーザーにエラーを通知する方法を紹介しよう。
■画像4
これはErrorProviderというコントロールを使っておる。エラーになったコントロールのすぐ横に表示されるし、マウスを持って行けばメッセージを見ることもできる。だから視覚的にエラーを通知する手段としてはメッセージよりも優れているかもしれん。
ErrorProviderは配置するとフォームの下にある部分にくっつく。
■画像5
プログラムは次のようになるぞ。TextBoxの数だけErrorProviderを配置するようなことはしなくてもいいのじゃ。
'体重をXにセットする。失敗する場合はメッセージを表示して処理を中断。 If Short.TryParse(WeightTextBox.Text, X) = False Then WeightTextBox.Focus() WeightTextBox.SelectAll() ErrorProvider1.SetError(WeightTextBox, "体重を正しく入力してください。") Return Else ErrorProvider1.Clear() End If |
■リスト5
これはVB2005から導入されたコントロールですね。
もう1つ最後に、UnhandledExceptionイベント(読み方:UnhandledException = アンハンドルドエクセプション)を紹介しよう。このイベントを使うとエラー処理をしていない部分で発生したエラーをすべて捕まえることができるのじゃ。しかもアプリケーションのどこで発生したエラーでも捕まえることができる。
へー!そんなすごいイベントがあったんですね!
これはアプリケーションレベルのイベントということでアプリケーションイベントと呼ばれているものの1つじゃ。試用するにはソリューションエクスプローラで「My Project」をダブルクリックしてアプリケーションページを開く。
■画像6
このページの右下に「アプリケーションイベントの表示」というボタンがあるからクリックするのじゃ。
ApplicationEvents.vbというファイルが自動的に生成される。
ここで左上のコンボボックスで「(MyApplication イベント)」を選択すると、右のイベントコンボボックスでいろいろなイベントを選択できるようになるから一覧からUnhandledExceptionを選択するのじゃ。これでイベントプロシージャが自動生成される。あとは普通のイベントのようにこの中にVBでプログラムを書くのじゃ。
■画像7
ただ、まぁ普通のエラートラップのようにきめ細かいことはできんので、ログに記録するとかユーザーにお詫びのメッセージを表示するとかそういう大雑把な目的で使うことがほとんどじゃがな。
知らなかったです。他にもいろいろなアプリケーションイベントがあるみたいですね。調べてみないと。
ただし。これもVB2005以上でないと使用できん機能じゃ。
投稿していただいた root.tools.programさん、ありがとうございます!
今後もみなさんからのプログラムの投稿をお待ちしています。