c# 事件(event) 介紹

什麼是Event?

簡單來說,Event其實就是通知(Notifications),如果某個方法(例如按按鈕)跟某個事件(另外一個方法)綁在一起,那做了這件事情之後就會觸發事件,也就是做了某個方法會觸發另一個方法。

Event的特點

  • 綁定Event的物件不需要清楚知道是哪個物件在處理Event的
  • Event透過EventArgs來傳遞資料

Event實作方式

如果要自己定義Event,要使用event關鍵字:

// Delegate
public delegate void WorkPerformedHandler(int hours, WorkType workType);

public enum WorkType
{
    GoToMeetings,
    Golf,
    GenerateReports
}

public event WorkPerformedHandler WorkPerformed;
// WorkPerformedHandler -> 是Delegate!!

呼叫Event

當作方法呼叫即可:

if(WorkPerformed != null)
{
    WorkPerformed(8, WorkType.GenerateReports);
}

因為WorkPerformed其實是delegate,所以我們也可以明確轉型成delegate來使用:

WorkPerformedHandler del = WorkPerformed as WorkPerformedHandler;
if(del != null)
{
    del(8, WorkType.GenerateReports);
}

以下是範例:

public delegate void WorkPerformedHandler(int hours, WorkType workType);
public class Worker
{
    public event WorkPerformedHandler WorkPerformed; // Event的定義
    public virtual void DoWork(int hours, WorkType workType)
    {
        // 做一些事,然後通知用戶方法已被呼叫
        OnWorkPerformed(hours, workType);
    }

    protected virtual void OnWorkPerformed(int hour, WorkType workType)
    {
       if(WorkPerformed != null)
       {
           WorkPerformed(hours, workType);
       }
    }

    // ------ 或是 ------

    protected virtual void OnWorkPerformed(int hour, WorkType workType)
    {
        WorkPerformedHandler del = WorkPerformed as WorkPerformedHandler;
        if(del != null)
        {
            del(hours, workType); // 呼叫Event
        }
    }
}

使用EventArgs

一般比較建議的做法是讓EventHandler有以下的格式:

public delegate void WorkPerformedHandler(object sender, EventArgs e);

sender代表啟動Event的方法,而EventArgs則是Event自帶的資料。

如果要自己定義這個EventArgs,我們可以自己寫一個類別,然後來繼承至System.EventArgs:

public class WorkPerformedEventArgs : System.EventArgs
{
    public int Hours {get; set; }
    public WorkType WorkType {get; set; }
}

使用EventHandler來註冊及觸發Event

如果沒看過EventHandler請參照: C# EventHandler 介紹

首先我們可以將delegateWorkPerformedHandler改寫成以下的程式碼:

public event EventHandler<WorkPerformedEventArgs> WorkPerformed;

我們可以透過以下的方式來註冊EventHandlerWorker類別:

var worker = new Worker();
worker.WorkPerformed += 
    new EventHandler<WorkPerformedEventArgs>(worker_WorkPerformed);

void worker_WorkPerformed(object sender, WorkPerformedEventArgs e)
{
    Console.WriteLine(e.Hours.ToString());
}

完整範例

接下來來改寫一下上面的Worker類別,然後做一個簡單的Demo:

//public delegate void WorkPerformedHandler(int hours, WorkType workType);
public class Worker
{
    public event EventHandler<WorkPerformedEventArgs> WorkPerformed; // Event的定義
    public event EventHandler WorkCompleted; 

    public virtual void DoWork(int hours, WorkType workType)
    {
        for(int i = 0;i < hours; i++)
        {
            // 每小時通知一次
            OnWorkPerformed(i + 1, workType);
        }
        // 結束時再通知一次
        OnWorkCompleted();
        
    }

    protected virtual void OnWorkPerformed(int hours, WorkType workType)
    {
       var del = WorkPerformed as EventHandler<WorkPerformedEventArgs>;
       if(del != null)
       {
           del(this, new WorkPerformedEventArgs(hours, workType))
       }
    }

    protected virtual void OnWorkCompleted()
    {
        var del = WorkCompleted as EventHandler;
        if(del != null)
        {
            del(this, EventArgs.Empty); // 如果不打算帶資料可以使用EventArgs.Empty
        }
    }
}

然後寫一個簡單的Console App來測試一下結果:

public class Program
{
    public static void Main()
	{
		var worker = new Worker();
        worker.WorkPerformed += new EventHandler<WorkPerformedEventArgs>(Worker_WorkPerformed);
        worker.WorkCompleted += new EventHandler(Worker_WorkCompleted);
        worker.DoWork(8, WorkType.GenerateReports);

        Console.Read();

	}

    public static void Worker_WorkPerformed(object sender, WorkPerformedEventArgs e)
    {
        Console.WriteLine(e.Hours + " " + e.WorkType);
    }

    public static void Worker_WorkCompleted(object sender, EventArgs e)
    {
        Console.WriteLine("Work Completed!");
    }
}

測試結果如下:

1 GenerateReports
2 GenerateReports
3 GenerateReports
4 GenerateReports
5 GenerateReports
6 GenerateReports
7 GenerateReports
8 GenerateReports
Work Completed!