委派演義(4) -- 委派與事件(一)

在第一篇曾經說到事件就是一個使用委派的常見例子,這篇咱們來探討一下關於委派與事件之間的相關知識。

       在第一篇曾經說到事件就是一個使用委派的常見例子,這篇咱們來探討一下關於委派與事件之間的相關知識。

 

       當我們自己寫類別或介面的時候會有機會要自己定義一些事件,事件宣告的最基本形式如下:

       Visual Basic

Event01

       C#

Event02

 

       上面這段程式宣告了一個名為 CommonEvent 的事件,而它使用的委派型別則是 .Net 本身已經有的 [ EventHandler 委派 ],我們常用的 [ Control.Click 事件 ]就是使用這個委派來宣告,從 MSDN 文件庫關於這個事件的宣告說明就可以瞭解某個事件是用哪個委派宣告的,如下圖:

2012-03-18_131249

(圖片資料來源: MSDN 文件庫 [ Control.Click 事件 ])

 

       在宣告事件的時候使用委派型別的原因是什麼?其實就是為了要定義事件委派函式的簽章,我們來看看 EventHandler 定義的簽章型式,如下圖:

2012-03-18_141736

(圖片資料來源: MSDN 文件庫 [ EventHandler 委派 ])

 

       從上圖就可以瞭解 EventHandler 的簽章,兩個傳入參數型別分別為 Object 和 EventArgs,無回傳值 ( 事件委派函式的簽章都是沒有回傳值的 ),這也就是我們常用的 Click 事件委派函式的參數會長那樣的原因。

 

       當然你也可能會自己定義委派,如以下的例子:


	Public Delegate Sub TestValueHandler(sender As Object, value As Boolean)
	Public Event TestValueChanged As TestValueHandler

 


		public delegate void TestValueHandler(Object sender, Boolean value);
		public event TestValueHandler TestValueChanged;

 

       瞭解完事件的宣告後,來寫一個類別展示一下事件的程式碼:

Visual Basic

 


Public Class EventSample
	Public Delegate Sub TestValueHandler(sender As Object, value As Boolean)
	Public Event TestValueChanged As TestValueHandler

	Public WriteOnly Property TestValue As Boolean
		Set(value As Boolean)
			OnTestValueChanged(value)
		End Set
	End Property

	Protected Overridable Sub OnTestValueChanged(value As Boolean)
		RaiseEvent TestValueChanged(Me, value)
	End Sub
End Class

 

  C#


	public class EventSample
	{
		public delegate void TestValueHandler(Object sender, Boolean value);
		public event TestValueHandler TestValueChanged;

		public Boolean TestValue
		{
			set { OnTestValueChanged(value); }
		}

		protected virtual void OnTestValueChanged(Boolean value)
		{
			TestValueChanged(this, value);
		}			

	}

 

       上面是一個很簡單的類別,在這個類別中藉由改變唯寫屬性 TestValue 的值來觸發事件,在 Visual Basic 中觸發事件需要使用 [ RaiseEvent 陳述式 ],C# 則是直接用事件名稱就行了。至於用個方法把 Method 包起來而且用 Overridable / virtual 宣告就是個人習慣啦,這樣做是在當有新的類別繼承它的時候可以靈活一點 ( 嗯,至少我只要覆寫子類別而不用大費周章的把父類別又拿出來瞎弄 )。

 

       那怎麼將方法委派到此事件?這應該也是老生常譚,許多人的文都寫過了,不過如果沒寫這一段感覺有點不完整,請各位再忍耐一下,這下又得分兩邊講了,用上面的類別來繼續做。

       Visual Basic:有兩種方式可以委派方法給事件

       (1) 使用  [ WithEvents 和 Handles 子句 ] (見連結文章中的 建立事件與事件處理常式的關聯 前半段),當使用了 WithEvents 宣告了物件後,可以在 Method 的宣告最末端使用 Handles 子句來將此 Method 委派給有使用 WithEvents 宣告的物件所屬之事件,這也是你在 Visual Studio 上使用控制項時,Visual Studio 幫你產生的程式碼的用法 (見前文 [ Windows Form Designer ]),如下程式碼:


Public Class Form1
	Private WithEvents sample2 As EventSample
	Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
		sample2 = New EventSample()
	End Sub

	Private Sub sample2_TestValueChanged(sender As Object, value As Boolean) Handles sample2.TestValueChanged
		MessageBox.Show(String.Format("測試值為:{0}", value))
	End Sub

	Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
		sample2.TestValue = True
	End Sub

	Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
		sample2.TestValue = False
	End Sub

End Class

     

 

       (2) 使用 [ AddHandler 陳述式] (見連結文章中的 建立事件與事件處理常式的關聯 後半段),如下程式碼:


Public Class Form1
	Private sample As EventSample
	Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
		sample = New EventSample()
		AddHandler sample.TestValueChanged, AddressOf sample_TestValueChanged
	End Sub

	Private Sub sample_TestValueChanged(sender As Object, value As Boolean)
		MessageBox.Show(String.Format("測試值為:{0}", value))
	End Sub


	Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
		sample.TestValue = True
	End Sub

	Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
		sample.TestValue = False
	End Sub

End Class

 

       (3) 關於以上 Visual Basic 的說明另請參考 [ WithEvents (Visual Basic) ] [ Handles 子句 (Visual Basic) ] [ AddHandler 陳述式 ] [ RemoveHandler 陳述式 ]

 

       C#:不得不說,真的又直覺多了,而且和前面把方法委派給委派執行個體的寫法幾乎一樣,只是對象換成事件。


	public partial class Form1 : Form
	{
		public Form1()
		{
			InitializeComponent();
		}

		private EventSample sample;
		private void Form1_Load(object sender, EventArgs e)
		{
			sample = new EventSample();
			sample.TestValueChanged +=new EventSample.TestValueHandler(sample_TestValueChanged);
			//這樣寫也行
			//sample.TestValueChanged += sample_TestValueChanged;
		}
		private void button1_Click(object sender, EventArgs e)
		{
			sample.TestValue = true;
		}

		private void button2_Click(object sender, EventArgs e)
		{
			sample.TestValue = false;
		}

		private void sample_TestValueChanged(object sender,Boolean value)
		{
			MessageBox.Show(String.Format("測試值為:{0}", value));
		}
	}

 

       上面這些程式碼也沒啥了不起的地方可以說嘴,就只是怎麼把方法委派給某個執行個體的事件,對大部份的人來講這一篇的東西真的沒什麼內容,因為我相信你閉著眼睛都寫的出來;不過,委派與事件的愛情故事到這邊還沒說完,有趣的事情還在後頭,下一篇再來聊吧。

 

      【附註】在Visual Basic 中有另外一種宣告事件的方式是直接宣告簽章,和蹂躪討論了一下 C# 應該是沒有類似的方式。


Public Event CustomEvent(send As Object, e As EventArgs)