さて、C# や VB でコードを短くするには限界があります。
という具体例を出しましょう。
まずは、先ほどの VB のコードを再掲します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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++ を使うとこの両方の条件を満たすことができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #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画面で収まる、ってのが理想的かなと思って実践中。