C# の Event をクソガキで理解する。

本当は、Moq の続きの記事を書こうと思ったのだが、Moq の次の対象が、Event だった。いろいろな記事やリファレンスを読んだけど、正直複雑で意味がわからなかったので、調べてみた。

多分めっちゃ簡単な実装をしてみた。[The Simplest C# Events Example Imaginable](https://www.codeproject.com/Articles/11541/The-Simplest-C-Events-Example-Imaginable)でやっと理解できたので、それを自分なりに実装してみた。

# Event を理解するクソガキアプリ

これはクソガキ通報アプリだ。クソガキ何かをしているのを、警察が観察しているけど、クソガキが法にふれたら刑務所にぶち込むというアプリだ。

```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Kusogaki
{ 

    public class Kusogaki
    {
        public event CriminalHandler Criminal;
        public EventArgs e = null;
        public delegate void CriminalHandler(Kusogaki k, EventArgs e);
        public void Observe()
        {
         while (true)
        {
                Console.WriteLine("#[Kusogaki] クソガキの行動を報告してください。");
                var action = Console.ReadLine();
                if (action == "万引き") {
                    if (Criminal != null)
                    {
                        Console.WriteLine($"#[Kusogaki] クソガキは {action} した。そらあかんやろ。通報や。");
                        Criminal(this, e); // 1. ここでイベント発生
                    }
                } else
                {
                    Console.WriteLine($"#[Kusogaki] クソガキは {action} したけどまあいいやろ。");
                }
        }

        }
    }

    public class Police
    {
        public void Subscribe(Kusogaki k)
        {
            k.Criminal += new Kusogaki.CriminalHandler(Arrest); // 2. クソガキ側のイベントハンドラに警察のメソッドを登録
        }
        private void Arrest(Kusogaki k, EventArgs e) // 3. 実行されるメソッド
        {
            Console.WriteLine($"#[Police] クソガキを連行します。");
        }
        
    }

    class Program
    {
        static void Main(string[] args)
        {
            var kusogaki = new Kusogaki();
            var police = new Police();
            police.Subscribe(kusogaki); // 0. イベント発生前に、クソガキを登録
            kusogaki.Observe();
        }
    }
}
```

コンソールアプリなので、理解しやすいだろう。結局ポイントは、イベントが発生するとどうなるかだ。イベント発生前に、0. の箇所で、クソガキの、Criminal Handler に、Police 側の Arrest メソッドを登録しておく。(2.)実際に、1. でイベントが発生すると、Event Handler に登録された、メソッドに処理が委譲される。


```
#[Kusogaki] クソガキの行動を報告してください。
鼻垂らした
#[Kusogaki] クソガキは 鼻垂らした したけどまあいいやろ。
#[Kusogaki] クソガキの行動を報告してください。
おばちゃんバカにした
#[Kusogaki] クソガキは おばちゃんバカにした したけどまあいいやろ。
#[Kusogaki] クソガキの行動を報告してください。
万引き
#[Kusogaki] クソガキは 万引き した。そらあかんやろ。通報や。
#[Police] クソガキを連行します。
```

クソガキがしっかり連行された。

# "+=" の謎

ちなみにメソッド登録時にこんなコードになっている。

```
k.Criminal += new Kusogaki.CriminalHandler(Arrest);
```

リファレンスを見てもよくわからないが、どうやらリストのように、ハンドラにいくつかのメソッドを登録できるっぽい。試してみよう。クソガキを連行して、刑務所にぶち込もう。

```csharp

    public class Police
    {
        public void Subscribe(Kusogaki k)
        {
            k.Criminal += new Kusogaki.CriminalHandler(Arrest);
            k.Criminal += new Kusogaki.CriminalHandler(Imprison);
        }
        private void Arrest(Kusogaki k, EventArgs e)
        {
            Console.WriteLine($"#[Police] クソガキを連行します。");
        }
        private void Imprison(Kusogaki k, EventArgs e)
        {
            Console.WriteLine($"#[Police] 懲役３年。お勤め頑張るんだぞ。");
        }
        
    }
```

実行してみる。

```
#[Kusogaki] クソガキの行動を報告してください。
ハゲおやじをバカにした
#[Kusogaki] クソガキは ハゲおやじをバカにした したけどまあいいやろ。
#[Kusogaki] クソガキの行動を報告してください。
屁をこいだ
#[Kusogaki] クソガキは 屁をこいだ したけどまあいいやろ。
#[Kusogaki] クソガキの行動を報告してください。
万引き
#[Kusogaki] クソガキは 万引き した。そらあかんやろ。通報や。
#[Police] クソガキを連行します。
#[Police] 懲役３年。お勤め頑張るんだぞ。
```

イベントを監視することで、無事、クソガキを逮捕できました。

# 犯行時間を加える

イベントが実行されるときに、何かの値を渡したいかもしれません。試しに、犯行時間を渡せるようにします。

```csharp

    public class TimeOfCrime : EventArgs
    {
        public DateTime Time
        {
            get; set;
        }
    }
```

こんな感じに EventArgs を継承したクラスを作ります。それを、先ほど EventArgs と書いていたところと置き換えます。

```csharp

    public class Kusogaki
    {
        public event CriminalHandler Criminal;
        public TimeOfCrime e = null; // 書き換え
        public delegate void CriminalHandler(Kusogaki k, TimeOfCrime e); // 書き換え
        public void Observe()
        {
         while (true)
        {
                Console.WriteLine("#[Kusogaki] クソガキの行動を報告してください。");
                var action = Console.ReadLine();
                if (action == "万引き") {
                    if (Criminal != null)
                    {
                        Console.WriteLine($"#[Kusogaki] クソガキは {action} した。そらあかんやろ。通報や。");
                        TimeOfCrime e = new TimeOfCrime();  // 書き換え
                        e.Time = DateTime.Now;
                        Criminal(this, e);
                    }
                } else
                {
                    Console.WriteLine($"#[Kusogaki] クソガキは {action} したけどまあいいやろ。");
                }
        }

        }
    }

    public class Police
    {
        public void Subscribe(Kusogaki k)
        {
            k.Criminal += new Kusogaki.CriminalHandler(Arrest);
            k.Criminal += new Kusogaki.CriminalHandler(Imprison);
        }
        private void Arrest(Kusogaki k, TimeOfCrime e) // 書き換え
        {
            Console.WriteLine($"#[Police] クソガキを連行します。犯行時間 {e.Time}"); // 書き換え
        }
        private void Imprison(Kusogaki k, TimeOfCrime e) // 書き換え
        {
            Console.WriteLine($"#[Police] 懲役３年。お勤め頑張るんだぞ。");
        }
        
    }
   
    : 
```

実行結果はこの通り。予想どおり！

```
#[Kusogaki] クソガキの行動を報告してください。
大阪のおばちゃんのシャツをバカにした
#[Kusogaki] クソガキは 大阪のおばちゃんのシャツをバカにした したけどまあいいやろ。
#[Kusogaki] クソガキの行動を報告してください。
ウォーターフォールをやった
#[Kusogaki] クソガキは ウォーターフォールをやった したけどまあいいやろ。
#[Kusogaki] クソガキの行動を報告してください。
万引き
#[Kusogaki] クソガキは 万引き した。そらあかんやろ。通報や。
#[Police] クソガキを連行します。犯行時間 2017/07/26 15:55:14
#[Police] 懲役３年。お勤め頑張るんだぞ。
```

無事クソガキが逮捕されて、犯行時間が記録されて、尚且つ皆さんの Event の理解につながれば幸いです。
コードは、[GitHub](https://github.com/TsuyoshiUshio/EventSample)に置いておきました。