------- Windows Phone 7手機開發、.Net培訓、期待與您交流! -------
委託和事件作爲winform,ASP.NET等不論是B/S,還是C/S開發的應用中是司空見慣的存在,我們初學者只有很好的掌握這些理論基礎,才能在後續的學習中靈活且高效的運用這些語言特性。所以現將筆記整理如下。
一、委託
委託是用來處理需用函數指針來處理的情況的
委託是完全面向對象的,是類型安全的
委託是可保存對方法的引用的類。與其他的類不同,委託類具有一個簽名,並且它只能對與其簽名匹配的方法進行引用。這樣,委託就等效於一個類型安全函數指針或一個回調
委託聲明定義一個從 System.Delegate 類派生的類。委託實例封裝了一個調用列表,該列表列出了一個或多個方法,每個方法稱爲一個可調用實體。對於實例方法,可調用實體由該方法和一個相關聯的實例組成。下面給出委託的實例化和調用示例。
using System;
namespace CSharpPractice.Delegate
{
delegate void D(int[] A);
class ArraySort
{
public static void DisplayMatrix(int[] A)
{ //打印矩陣
foreach (int i in A) Console.Write("{0,5} ", i);
Console.WriteLine();
}
public static void GeneralSort(int[] A, D sort)
{
sort(A);
Console.WriteLine("升序數組: ");
foreach (int i in A) Console.Write("{0,5} ", i);
Console.WriteLine();
}
public static void BubbleSort(int[] A) //冒泡算法
{
int i, t;
int N = A.Length; //獲取數組A的長度N
for (int loop = 1; loop <= N - 1; loop++)//外循環進行N-1輪比較
{
for (i = 0; i <= N - 1 - loop; i++) //內循環兩兩比較,大數下沉
if (A[i] > A[i + 1])
{
t = A[i];
A[i] = A[i + 1];
A[i + 1] = t;
}
}
}
public static void SelectSort(int[] A) //選擇算法
{
int i, t, MinI;
int N = A.Length; //獲取數組A的長度N
for (int loop = 0; loop <= N - 2; loop++) //外循環進行N-1輪比較
{
MinI = loop;
for (i = loop; i <= N - 1; i++) //內循環中在無序數中找最小值
if (A[i] < A[MinI]) MinI = i;
//最小值與無序數中的第一個元素交換
t = A[loop];
A[loop] = A[MinI];
A[MinI] = t;
}
}
static void Main()
{
int[] A = new int[10];
Random rNum = new Random();
//數組A賦值(0~100之間的隨機數)
for (int i = 0; i < A.Length; i++) A[i] = rNum.Next(100);
Console.WriteLine("原始數組: ");
DisplayMatrix(A);
D d1 = new D(ArraySort.BubbleSort);//創建委託實例,指向冒泡算法
Console.Write("冒泡算法---");
GeneralSort(A, d1);
D d2 = new D(ArraySort.SelectSort);//創建委託實例,指向選擇算法
Console.Write("選擇算法---");
GeneralSort(A, d2);
Console.ReadKey();
}
}
}
2.匿名方法委託
無需先聲明類或結構以及與委託匹配的方法,而是在創建委託的實例時,直接聲明與委託匹配的方法的代碼塊(匿名方法),匿名方法委託例示。
using System;
namespace CSharpPractice.Delegate
{
// 聲明委託
delegate void Printer(string s);
class TestClass
{
// 與命名委託相關的方法:
static void DoWork(string k)
{
Console.WriteLine(k);
}
static void Main()
{
// 使用匿名方法實例化delegate類:
Printer p = delegate(string j)
{
Console.WriteLine(j);
};
// 匿名delegate調用結果:
p("使用匿名方法的委託的調用.");
// 使用"DoWork"方法對delegate實例化:
p = new Printer(TestClass.DoWork);
// 傳統delegate調用結果:
p("使用命名方法的委託的調用.");
Console.ReadLine();
}
}
}
3.多播委託
委託也可以包含多個方法,這種委託稱爲多播委託
如果調用多播委託實例,則按順序依次調用多播委託實例封裝的調用列表中的多個方法
聲明多播委託時,其返回類型必須爲void,因爲無法處理多次調用的返回值,而且不能帶輸出參數(但可以帶引用參數)
多播委託通過 + 或 += 向多播委託實例封裝的調用列表中添加方法;通過 – 或 -= 從多播委託實例封裝的調用列表中刪除方法。
using System;
namespace CSharpPractice.Delegate
{
delegate void D(int x);
class C
{
public static void M1(int i)
{
Console.WriteLine("C.M1: " + i);
}
public static void M2(int i)
{
Console.WriteLine("C.M2: " + i);
}
public void M3(int i)
{
Console.WriteLine("C.M3: " + i);
}
}
class Test
{
static void Main()
{
D cd1 = new D(C.M1);
cd1(-1); // call M1
D cd2 = new D(C.M2);
cd2(-2); // call M2
D cd3 = cd1 + cd2;
cd3(10); // call M1 then M2
cd3 += cd1; cd3(20); // call M1, M2, then M1
C c = new C();
D cd4 = new D(c.M3);
cd3 += cd4; cd3(30); // call M1, M2, M1, then M3
cd3 -= cd1; // remove last M1
cd3(40); // call M1, M2, then M3
cd3 -= cd4; cd3(50); // call M1 then M2
cd3 -= cd2; cd3(60); // call M1
cd3 -= cd2; // impossible removal is benign
cd3(60); // call M1
cd3 -= cd1; // invocation list is empty so cd3 is null
// cd3(70); // System.NullReferenceException thrown
cd3 -= cd1; // impossible removal is benign
Console.ReadLine();
}
}
}
4.委託的兼容性
D和M的參數數目相同,且各自對應參數具有相同的ref或out修飾符;(D指委託,M指方法)
對於每個ref或out參數,D中的參數類型與M中的參數類型相同。
存在從M的返回類型到D的返回類型的標識或隱式引用轉換。即允許方法具有的派生返回類型比委託中定義的更多(協變)。
每一個值參數(沒有 ref 或 out 修飾符的參數)都存在從D中的參數類型到M中的對應參數類型的標識或隱式引用轉換。允許方法具有的派生參數類型比委託類型中的更少(逆變)
using System;
namespace CSharpPractice.Delegate
{
class Mammals
{
//...
}
class Dogs : Mammals
{
//...
}
class Program
{ // 定義委託
public delegate Mammals HandlerMethod();
public delegate void HandlerMethod1(Mammals m);
public delegate void HandlerMethod2(Dogs d);
public static Mammals FirstHandler()
{
Console.WriteLine("first handler");
return null;
}
public static Dogs SecondHandler()
{
Console.WriteLine("second handler");
return null;
}
public static void ThirdHandler(Mammals m)
{
Console.WriteLine("third handler");
}
static void Main()
{
HandlerMethod handler1 = FirstHandler;
handler1();
//協變
HandlerMethod handler2 = SecondHandler;
handler2();
Mammals m = new Mammals();
HandlerMethod1 handler11 = ThirdHandler;
handler11(m);
//逆變
Dogs d = new Dogs();
HandlerMethod2 handler22 = ThirdHandler;
handler22(d);
Console.ReadKey();
}
}
}
二、事件
類或對象可以通過事件(event)向其他類或對象通知發生的相關事情。發送(或引發)事件的類稱爲“發行者”(生產者),接收(或處理)事件的類稱爲“訂戶”(消費者)。
事件是一種使對象或類能夠提供通知的成員。客戶端可以通過提供事件處理程序(event handler)爲相應的事件添加可執行代碼。
事件是對象發送的消息,以發信號通知操作的發生。操作可能是由用戶交互(例如鼠標單擊)引起的,也可能是由某些其他的程序邏輯觸發的。
2.事件特點
發行者確定何時引發事件,訂戶確定執行何種操作來響應該事件
一個事件可以有多個訂戶。一個訂戶可處理來自多個發行者的多個事件
沒有訂戶的事件永遠不會被調用
事件通常用於通知用戶操作,例如,圖形用戶界面中的按鈕單擊或菜單選擇操作
如果一個事件有多個訂戶,當引發該事件時,會同步調用多個事件處理程序
可以利用事件同步線程
在 .NET Framework 類庫中,事件是基於 EventHandler 委託和 EventArgs 基類的
3.事件處理機制
事件實際上是委託的一種特殊形式。C# 使用一種委託模型來實現事件。事件模型分爲事件生產者和事件消費者,其處理機制大致可以分爲下列4步
在事件生產者類中聲明一個事件成員,即某種事件處理委託(簡稱爲事件委託)的實例(多播事件委託實例);
在事件消費者類中聲明與事件委託相匹配的事件處理方法;
通過“+=”向多播事件委託實例封裝的調用列表中添加事件處理方法,或通過“-=”從多播事件委託實例封裝的調用列表中刪除事件處理方法;
在事件生產者類中添加有關發生事件的代碼,即當滿足某種條件時(發生事件),則調用委託,即調用多播事件委託實例封裝的調用列表中添加的事件處理方法。如果沒有訂閱,即事件實例爲Null,則不作任何處理。
下面給出實現事件步驟的代碼。算是對初步掌握事件一個總結吧。
<span style="font-size:18px;">using System;
using System.Collections;
namespace CSharpPractice.Event
{
//步驟1:聲明提供事件數據的類。
public class NameListEventArgs : EventArgs
{
public string Name { get; set; }
public int Count { get; set; }
public NameListEventArgs(string name, int count)
{
Name = name;
Count = count;
}
}
//步驟2:聲明事件處理委託。
public delegate void NameListEventHandler(object source, NameListEventArgs args);
//步驟3:聲明引發事件的類(事件生產類)。
public class NameList
{
ArrayList list;
//步驟4:在事件生產類中,聲明事件。
public event NameListEventHandler nameListEvent;
public NameList()
{
list = new ArrayList();
}
public void Add(string Name)
{
list.Add(Name);
//步驟5:在事件生產類中,實現產生事件的代碼。
if (nameListEvent != null)
{
nameListEvent(this, new NameListEventArgs(Name, list.Count));
}
}
}
//步驟6:聲明處理事件的類(事件消費類)。
public class EventDemo
{
//步驟7:在事件消費類中,聲明事件處理方法。
public static void Method1(object source, NameListEventArgs args)
{
Console.WriteLine("列表中增加了項目:{0}", args.Name);
}
//步驟7:在事件消費類中,聲明事件處理方法。
public static void Method2(object source, NameListEventArgs args)
{
Console.WriteLine("列表中的項目數:{0}", args.Count);
}
public static void Main()
{
NameList nl = new NameList();
//步驟8:在事件消費類中,訂閱或取消事件。
nl.nameListEvent += new NameListEventHandler(EventDemo.Method1);
nl.nameListEvent += new NameListEventHandler(EventDemo.Method2);
nl.Add("張三");
nl.Add("李四");
nl.Add("王五");
Console.ReadLine();
}
}
}</span>
寫了這麼久,好累啊,該出去走走了,放鬆放鬆。。
------- Windows Phone 7手機開發、.Net培訓、期待與您交流! -------