匿名メソッド

 
オーバーロード(overload)とオーバーライド(override)、インデクサ(indexer)、プロパティ(property)、イベント(event)、リフレクション(reflection)...
 
実は色々こそっと勉強して、記事にしようと思ったキーワードはいくらでもあった。
が、参考にしている@IT等々がうまーく書きすぎている+自分自身がステキなサンプルコードを提供できない、などの理由で一人で悦に浸って終わっていた。
 
ので、実は

@IT 連載 改訂版 C# 入門
http://www.atmarkit.co.jp/fdotnet/csharp_abc2/index/index.html

は、一通り学び終えていたりする。
上記の内容を完璧に会得したか、と言われれば答えは迷わずノーなのだが。
もちろんそれは、学んだ内容を自分で生かしてプログラミングしていないから。
 
とはいえ、有用な内容で自作サンプルプログラムを考案できなかったのだからしょうがない。
上記は今後紹介することが出来たらいいな、ということでゴメンナサイ。
 

@IT 「連載:改訂版 C#入門」続編
C# 2.0 入門
http://www.atmarkit.co.jp/fdotnet/csharp20/index/index.html

 
C# は 1.x と 2.0 で全く異なるという説明がなされている。
以前、別Blogでも少し取り上げたが、本当に目から鱗というか、驚き以外の何物でもなかった。
紹介されている C#2.0 のプログラミングコードが、あまりにもこれまでの常識を覆す内容だからだ。
(私の勉強不足はさておくとして
 
と、仰々しく書くことでこれまでのサボりを誤魔化しつつ、さらっと C#2.0 を見て行きたい。
内容はさらっとは行かない濃密さである。
 

連載:C# 2.0入門
第1回 総論:C# 2.0らしいプログラミングとは:
http://www.atmarkit.co.jp/fdotnet/csharp20/csharp20_01/csharp20_01_01.html

 
読者は上記に目を通した(または、通す必要がないほどC#2.0に長けている)と仮定する。
自分でプログラム書いてみました。
すっごいウソのプログラムだけど。
 
まずは、匿名メソッドの恩恵を受けられなかった悲しいコード。
 

namespace notAnonymousDelegate
{
    class Program
    {
        static void Main(string[] args)
        {
            String name = "ロウ";

            // クィンとお話するとステータスはどうなる?
            pp.status = 0;
            pp.talkToQuin(name);
            Console.WriteLine("{0}がクィンとお話したぉ:Status - {1}", name, pp.status);

            // クリスとお話するとステータスはどうなる?
            pp.status = 0;
            pp.talkToChris(name);
            Console.WriteLine("{0}がクリスとお話したぉ:Status - {1}", name, pp.status);
        }
    }

    class pp
    {
        public static int status;

        public static void talkToChris(String name)
        {
            talk(name, true);
        }

        public static void talkToQuin(String name)
        {
            talk(name, false);
        }

        private static void talk(
            String name,
            bool isChris
        )
        {
            // クリスと話してるなら嫌われちゃえ
            if (isChris)
            {
                status--;
                status--;
                status--;
                status--;
                status--;
            }
            // クィンなら少し好感度アップ
            else
            {
                status++;
            }

            /*
             * name で指定した話しかけた側のキャラクタのトーク経験値アップ
             * (未実装)
             */

            // クリスと話してるなら更に嫌われちゃえ!
            if (isChris)
            {
                status--;
                status--;
                status--;
                status--;
                status--;
            }
        }
    }
}

 
実行した結果は以下。
 

ロウがクィンとお話したぉ:Status - 1
ロウがクリスとお話したぉ:Status - -10

 
内容はあまり気にしないこと。
で、これを匿名メソッドを使うとこうなる。
 

    class Program
    {
        static void Main(string[] args)
        {
            String name = "ロウ";

            // クィンとお話するとステータスはどうなる?
            pp.status = 0;
            pp.talkToQuin( name );
            Console.WriteLine( "{0}がクィンとお話したぉ:Status - {1}",name, pp.status );

            // クリスとお話するとステータスはどうなる?
            pp.status = 0;
            pp.talkToChris( name );
            Console.WriteLine("{0}がクリスとお話したぉ:Status - {1}", name, pp.status);
        }
    }

    class pp
    {
        delegate void MethodInvoker();
        static public int status;

        // 優しいクィンとお話をする
        public static void talkToQuin(String name)
        {
            talk(name, delegate() { }, delegate() { status++; });
        }

        // ツンデレなクリスとお話をする
        public static void talkToChris(String name)
        {
            talk(
              name,
                // hating
              delegate()
              {
                  // めちゃめちゃ嫌われてみる
                  status--;
                  status--;
                  status--;
                  status--;
                  status--;
              },
                // statusChanger
              delegate()
              {
                  // 更にめちゃめちゃ嫌われてみる
                  status--;
                  status--;
                  status--;
                  status--;
                  status--;
              }
            );
        }

        // お話メソッド
        private static void talk(
            String name,
            MethodInvoker hating,
            MethodInvoker statusChanger
        )
        {
            hating();            // どんだけ嫌われるの?っつー話

            /*
             * name で指定した話しかけた側のキャラクタのトーク経験値アップ
             * (未実装)
             */

            statusChanger();    // ステータス変化
        }
    }

 
結果は同じ。
 
一応想定通りに動かすことができました。
ふぃー。
 
talk() メソッドの汎用性が格段に上がっている。
はじめのサンプルだと、ある二人としか会話できない仕組みで、それを拡張するのは手間。
やりようがないとは言わないけど、こんな単純なサンプルでも、ごちゃごちゃいじる必要があるのは明確。
 
結果としてはじめのサンプルから二つ目のサンプルに昇華することで、if文が消えた。
この辺りが C#2.0 での大きな改革であり、可能性のような気がする。
@IT記事でも、クラスの位置づけが低下して、オブジェクト指向の要素というより入れ物に近くなってきた、と書いてあるが、まさにその通りだ。
 
なにはともあれ、引数として関数(処理そのもの)を渡す、という発想に脱帽。
このパターンは色々応用が利く設計手法になると思う。
if から想像することすらできる。
ある関数で渡されるパラメータごとに部分的に処理を変えたい、というような if が見当たるなら今回の方法でキレイに対応できる。
まさに今回のパターン。
一つ目の匿名メソッドを取り入れてないサンプルと、二つ目の匿名メソッド導入版サンプルで、talk() メソッドの可読性とコード量を比較していただければ一目瞭然。