さて、C# や VB でコードを短くするには限界があります。
という具体例を出しましょう。
まずは、先ほどの VB のコードを再掲します。
Public Sub InitStatusBar() SETSTATUS(Button1, "先頭のボタンです") SETSTATUS(Button2, "真ん中のボタンです") SETSTATUS(Button3, "一番舌のボタンです") SETSTATUS(ComboBox1, "項目を選択します") SETSTATUS(TextBox1, "名前を入力します") SETSTATUS(TextBox2, "年齢を入力します") SETSTATUS(TextBox3, "住所を入力します") End Sub Private _statusdic As New Dictionary(Of Control, String) ''' <summary> ''' フォーカス時のメッセージを設定 ''' </summary> ''' <param name="c"></param> ''' <param name="msg"></param> ''' <remarks></remarks> Public Sub SETSTATUS(ByVal c As Control, ByVal msg As String) _statusdic.Add(c, msg) AddHandler c.MouseEnter, AddressOf COM_MouseEnter 'MouseEnter AddHandler c.MouseLeave, AddressOf COM_MouseLeave 'MoueeLeave End Sub Public Sub ChangeFocus(ByVal sender As Object, ByVal b As Boolean) If b = False Then ToolStripStatusLabel1.Text = "" Else ToolStripStatusLabel1.Text = _statusdic(CType(sender, Control)) End If End Sub Private Sub COM_MouseEnter(ByVal sender As System.Object, ByVal e As System.EventArgs) ChangeFocus(sender, True) End Sub Private Sub COM_MouseLeave(ByVal sender As System.Object, ByVal e As System.EventArgs) ChangeFocus(sender, False) End Sub
これでも十分短いですよね。しかも Dictonary を使っているから効率が良さそうです。
ですが、Dictonary を使っているために、イベントがワンクッション遅くなっています。大抵のイベントはこれでいいのですが、高速化が問題になるときは、この【ワンクッション】が致命的になります。
じゃあ、ってんで VB や C# の場合は冗長パターンに書き直す(あるいは、スクリプトを使ってコードを自動生成する)ことになるんですが、C++ を使うとこの両方の条件を満たすことができます。
#define SETEVENT( cn ) \ this->cn->MouseLeave += gcnew System::EventHandler(this, &Form1::cn##_MouseLeave); \ this->cn->MouseEnter += gcnew System::EventHandler(this, &Form1::cn##_MouseEnter); #define SETSTATUS( cn, msg ) \ private: System::Void cn##_MouseEnter(System::Object^ sender, System::EventArgs^ e) { \ this->ToolStripStatusLabel1->Text = msg; \ } \ private: System::Void cn##_MouseLeave(System::Object^ sender, System::EventArgs^ e) { \ this->ToolStripStatusLabel1->Text = ""; \ } private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e) { // ステータスバーのイベントを設定 SETEVENT( Button1 ); SETEVENT( Button2 ); SETEVENT( Button3 ); SETEVENT( ComboBox1 ); SETEVENT( TextBox1 ); SETEVENT( TextBox2 ); SETEVENT( TextBox3 ); } /// ステータスバーの設定 SETSTATUS( Button1, "先頭のボタンです"); SETSTATUS( Button2, "真ん中のボタンです"); SETSTATUS( Button3, "一番下のボタンです"); SETSTATUS( ComboBox1 , "項目を選択します"); SETSTATUS( TextBox1, "名前を入力します"); SETSTATUS( TextBox2, "年齢を入力します"); SETSTATUS( TextBox3, "住所を入力します");
既にマジックなコードになっていますが、見かけ上は分かりやすいですよね。
保守という点では、SETEVENT と SETSTATUS でマクロが2つに分かれていますが、先の VB のコードのように
・一時的な List や Dictonary を使わない(メモリ効率)
・イベント呼び出しにワンクッション置かない(高速化)
という条件が満たされています。
ここでトリッキーな使い方をしているのが #define のところで、ボタン名などの名前からイベント名を作成しています。イベント名を個別に作成するので、見掛け上は 1 行のコードですが、実際は複数行書かれています。
C/C++ の場合は、割とこのようなコードを書くのが通常のパターンなのですが、残念ながら VB や C# の場合は、このパターンが使えないんですよね。
ちなみに、この手のマクロはコーディング時の副作用(特にコンパイルができないとか)が大きいので、使い終わったら、
#undef SETEVENT
とやって、消しておくとモアベターです。
この例なら、次のコードでいけそうな気がしますが、
やっぱりC++じゃないとダメなケースってありますか?
Public Sub SETSTATUS(ByVal c As Control, ByVal msg As String)
AddHandler c.MouseEnter, Sub(sender, e) ToolStripStatusLabel1.Text = msg
AddHandler c.MouseLeave, Sub(sender, e) ToolStripStatusLabel1.Text = “”
End Sub
おおッ!!! Thanks です。
VB 2010 ならラムダ式でOKですね。
実は、いまの開発が VB 2005 なものでラムダ式が使えないんですよね…ってなオチだったり。
そういえば、ラムダ式内で使われている msg って、どうやって参照しているんでしょうね?と不思議に思ったり。
SETSTATUS 関数で渡される引数だから、単なる変数の参照だと他の呼び出しのときに違っちゃうし。埋め込みなのかな?
# msg の内容を変えても、期待通り動くことは確認済み
コードを短くする技法ためになります。
―追伸―
一番初めのVBのコード内にある、「STATMSG」クラスは使用していないから削除してもいいのでは?
あ、修正前の実コードのゴミですね。STATMSG は削除しておきましょう。
メソッドを短く ≒ 1画面で収まる、ってのが理想的かなと思って実践中。