C#のイベントとデリゲート(delegate)の違い

概要
1. イベントについて
2. イベントとデリゲートの違いについて
2-1. メソッド型(戻り値・引数の違い)
2-2. イベントの実行制限

1.イベントについて

イベントはデリゲートの仕組みを利用して実現しています。
デリゲートに関しては、別で説明していますので、ここではイベントとデリゲートの違いの部分について説明します。

イベントとは、マウスクリック等のイベントがOSからそのクラスへと伝えられた時に、あらかじめ登録されたメソッドを実行する機能です。
そのメソッドの事をイベントハンドラと呼びます。
イベントハンドラはデリゲートの仕組みを利用して、登録を行います。

// デリゲート宣言
public delegate void SampleEventHandler(object sender, EventArgs e);

class Class1
{
    // イベント変数の宣言
    public event SampleEventHandler sampleEvent;
    // 適当なイベントハンドラ
    public void handler(object o, EventArgs e)
    {
        Console.WriteLine("handler called");
    }
}

static void Main(string[] args)
{
    // イベント変数・ハンドラの生成
    Class1 target = new Class1();
    // イベント変数にハンドラを登録
    target.sampleEvent += new SampleEventHandler(target.handler);
    // イベントの実行
    target.sampleEvent(target, EventArgs.Empty);
}

まずイベント変数というものをデリゲートを拡張して作ります。
デリゲート宣言をした後、イベント変数を宣言してます。
ここでの型はデリゲートによって与えます。
それにevent修飾子を付けることで、イベント変数を宣言します。

main内ではイベント変数に対して、ハンドラを登録しています。
これはデリゲートにメソッドを格納しているのと全く同じです。
イベントの実行もデリゲートの実行と何も変わりありません。

以上から見れるように、イベントとは基本的にデリゲートであると言えます
よってデリゲートの仕組みを理解すれば、イベントの仕組みをほぼ理解したと言えます。
ただしいくつか違う点もありますので、その部分を解説します。


2.イベントとデリゲートの違いについて

イベントはデリゲートの型宣言を利用して宣言しますので、必ず事前にデリゲートを宣言しておく必要があります
後は基本的にデリゲートと同じように扱う事ができます。
しかしいくつかの違いもあります。

2-1.メソッド型(戻り値・引数の違い)

デリゲートのメソッド型は自由に生成できましたが、イベントはイベントハンドラの型がある程度決まっている為、デリゲートのメソッド型もそれに合わせる必要があります。

// イベントハンドラ
public void handler(object o, EventArgs e)
{
    Console.WriteLine("handler called");
}

典型的なイベントハンドラは、

・戻り値なし
・object型1つ、EventArgs型1つの計2つの引数

を持ちます。
よってデリゲートも

delegate void SampleEventHandler(object sender, EventArgs e);

このような感じでメソッド型を合わせなければなりません。
ただしEventArgsに関しては、これを派生させたクラスであれば、指定可能です。

// EventArgsの派生クラス例
public class SampleEventArgs : EventArgs
{
    public string message;
}


2-2.イベントの実行制限

イベントの実行は、デリゲートの実行と同じですが、その実行処理を置く場所に制限があります。
デリゲートは何処でも自由に置くことができますが、イベントはそのイベント変数が宣言されているクラス内にしか置くことができません。

・デリゲート・・・別クラスの中から実行可能
・イベント・・・同一クラスの中からのみ実行可能

という事です。これはイベントを発生させるクラスの中にイベント宣言も持っているべきという仕様から来ています。
クリックイベントは、それが宣言されているボタンクラスの中で実行されるべきであって、別のボタンクラス等から実行されるべきではないからです。
例えイベント宣言されたクラスの派生クラス内からであっても、実行はできないようになっています。


2-3.イベント登録時に特定のメソッドを実行可能

public static event SampleEventHandler sampleEvent
{
    add
    {
        Console.WriteLine("add called");
    }

    remove
    {
        Console.WriteLine("remove called");
    }
}

↑のようにイベント宣言時にadd/removeメソッドを定義する事ができます。
addメソッドは、+演算子を使ってイベントハンドラを登録した時にremoveメソッドは、-演算子を使ってイベントハンドラを削除した時に実行されます。

イベント登録・削除に実行させたい処理があれば、この中に記述可能です。

以上がデリゲート・イベントの違いになりますが、実行処理を置く場所にさえ気をつければ、基本的には同じだと言えますので、まずはデリゲートの仕組みを理解するようにすれば、イベントもすぐに理解できると思います。

以上になります。


参考:
C#のデリゲート(delegate)と関数ポインタの違い - enoのはてなダイアリー
連載:C#入門 第13回 言語に内蔵されたイベント機能