C# Actionとは何ぞ?

Web検索するとよく判らない日本語になってないカタカナでいろいろ説明されたものが沢山ヒットする。中でもマイクロソフトでは、「Action<T> 代理人」とある。delegateというものの一種らしい。メソッドを変数のようにできるとある。そこでIC7851RC2の中に置き換えられそうなコードがあったのでActionで同じ動作になるコードに書き換えてみた。こういうことらしい。

ボタンをクリックしたらボタンに対応した機能がON/OFFになるというものだ。 

else if を使う
        private void InversionButton_Click(object sender, EventArgs e)
        {
            var bt = (Button)sender;
            if (bt == FreqRIT) Set_RIT(!RIT);
            else if (bt == FreqXIT) Set_dTX(!dTX);
            else if (bt == DualWatch) Set_DW(!DW);
            else if (bt == VFO_Split) Set_Split(!SPLIT);
            else if (bt == AntTuner) Set_Tuner(!Tuner);
            else if (bt == ScopeFix) Set_ScopeMode(!FIX[VFO]);
            else if (bt == QRS_Button) Set_QRS(!QRS);
            else if (bt == APO_Button) Set_APO(!APO);
            else if (bt == Power) Set_TrxPower(!PwrOn);
            else if (bt == AutoTune) Set_AutoTuneStart(!ATS);
            else if (bt == CompBt) Set_CompEnable(!Comp); 
            else if (bt == StlBt) Set_SideToneLimit(!STL); 
            else if (bt == HpBt) Set_Headphone(!HP);
            else if (bt == MoniBt) Set_Monitor(!Monitor);
            else if (bt == ScopeB0) Set_ScTxSigs(!ScTxSig);
            else if (bt == ScopeB3) Set_ScMarkerPoint(!ScMrkPnt);
            else if (bt == ScopeB4) Set_ScSigsWidth(!ScSigsWidth);
            else if (bt == ScopeB6) Set_ScSpectrum(!ScSpec);
            else if (bt == ScopeB7) Set_WaterFall(!ScWF);
            else if (bt == ScopeB11) Set_DualDisplayType(!ScDDT);
            else if (bt == ScopeB12) Set_SwitchLink(!ScSL);
            else if (bt == DisTypeBt) Set_DisplayType(!DisType);
            else if (bt == MtrHldBt) Set_MeterPeakHold(!MtrHld);
            else if (bt == MtrExpBt) Set_MeterExpType(!MtrExp);
        }

C#のswitchでするには、bt.Nameの文字列一致でないとできないしbreak必須でコーディングがくどいのでこのような形にしたが個人的にelse if の羅列は好きでない。こういうのどうにかならいものかと思っていた。

Dictionary でAction<T>を使う
        private void InversionButton_Click(object sender, EventArgs e)
        {
            var bt = (Button)sender;
            var ButtonAction = new Dictionary<Button, (Action<bool> perform, bool bo)>() {
                { FreqRIT, (Set_RIT, RIT) }, 
                { FreqXIT, (Set_dTX, dTX) }, 
                { DualWatch, (Set_DW, DW) }, 
                { VFO_Split, (Set_Split, SPLIT) }, 
                { AntTuner, (Set_Tuner, Tuner) },
                { ScopeFix, (Set_ScopeMode, FIX[VFO]) },
                { QRS_Button, (Set_QRS, QRS) }, 
                { APO_Button, (Set_APO, APO) }, 
                { Power, (Set_TrxPower, PwrOn) },
                { AutoTune, (Set_AutoTuneStart, ATS) }, 
                { CompBt, (Set_CompEnable, Comp) },
                { StlBt, (Set_SideToneLimit, STL) },
                { HpBt, (Set_Headphone, HP) }, 
                { MoniBt, (Set_Monitor, Monitor) },
                { ScopeB0, (Set_ScTxSigs, ScTxSig) }, 
                { ScopeB3, (Set_ScMarkerPoint, ScMrkPnt) }, 
                { ScopeB4, (Set_ScSigsWidth, ScSigsWidth) },
                { ScopeB6, (Set_ScSpectrum, ScSpec) }, 
                { ScopeB7, (Set_WaterFall, ScWF) }, 
                { ScopeB11, (Set_DualDisplayType, ScDDT) },
                { ScopeB12, (Set_SwitchLink, ScSL) },
                { DisTypeBt, (Set_DisplayType, DisType) },
                { MtrHldBt, (Set_MeterPeakHold, MtrHld) },
                { MtrExpBt, (Set_MeterExpType, MtrExp) } };
            if (!ButtonAction.ContainsKey(bt)) return;
            var bo = !ButtonAction[bt].bo;
            ButtonAction[bt].perform(bo);
        }

Dictionary のキーをボタンにして値にAction<T>のメソッド名とそのメソッドに必要な引数をタプルにした。こんな使い方はあまりないと思うけれども便利に使えることが分かった。個人的にはこちらが好みである。ただし動作の違いは無いので好みの問題ではないだろうか。

※いろいろ調べていたら厳密にはTaskクラスというのを使ってこれを実行しているので動作が違うらしい。IC7851RC2のような非同期処理多発のアプリケーションにはいいのかもしれない。

コメント