本文內容:
1. 委托定義
2. 申明委托
3. 委托的實例化
4. 委托的調用
5. 匿名方法
6. 委托程序實例
7. 事件定義
8. 事件的申明
9. 事件的調用
10. 事件訂閱與移除
11. 事件程序實例
12. 標准化事件的設計
1. 委托定義
委托(Delegate)是C#或者.NET中表示強類型方法的特殊類型。比較接近於C語言中的函數指針。(指向函數入口地址的數據類型)。讀到這里說下C語言的兩個概念:指針函數和函數指針。
指針函數是指帶指針的函數,即本質是一個函數。函數返回類型是某一類型的指針。
int *fun(x);
函數指針是指向函數的指針變量,即本質是一個指針變量。
int (*f) (int x); /* 聲明一個函數指針 */
f=function; /* 將function函數的首地址賦給指針f */
因此由上面可以看出,委托其實是一種封裝好的函數指針。
2. 申明委托
申明委托使用delegate關鍵字,語法是:
[<修飾符>] delegate <返回類型> <委托名> ([<形參表>])
修飾符:new、public、protected、internal、private。如果在namespace下,只能用public,internal 。
返回類型:與方法的返回類型相同
委托名:與其他類型的標識符要求相同,常用handler作后綴
形參表:與方法的形參表的要求相同
舉例:delegate void D1(int i, double d);
3.委托的實例化
由於委托類似方法指針,申明委托時給出的返回類型和形參表,實際上是指該委托所能指向或者封裝的方法的返回類型和參數表。
凡是與委托的返回類型和形參表相同的方法,不管是靜態的還是實例的,稱為與委托的類型兼容,都可以被該委托的實例封裝。
委托類型定義后,可以像其他類型一樣申明該類型的變量,並對變量實例化。
對委托進行實例化的寫法為:
new <委托類型名> (<表達式>)
這里,表達式可以是一個兼容的靜態方法、實例方法、或者一個委托實例。
例如:
delegate void D(int x); //申明委托
class C
{ public static void M1(int i) {...}
public void M2(int i) {...}
}
class Test
{ static void Main()
{ D cd1 = new D(C.M1); // 用靜態方法
C t = new C(); // 實例化有關類
D cd2 = new D(t.M2); // 用實例方法
D cd3 = new D(cd2); // 用一個委托實例
}
}
從某個方法創建一個委托實例時,該委托實例將封裝此方法,此時,它的調用列表只包含一個進入點。 可用 +和 += 運算符組合委托實例,用 -和 -= 運算符將一個委托從委托組合中移除。
當組合兩個或多個非空委托實例時,它們的調用列表被連接在一起(按照左操作數在先、右操作數在后的順序)以組成一個新的調用列表,它包含了兩個或更多個“進入點”。
例如:委托的組合及調用列表
delegate void D(int x);
class C
{ public static void M1(int i) {...}
public static void M2(int i) {...}
}
class Test
{ static void Main()
{ D cd1 = new D(C.M1); // M1
D cd2 = new D(C.M2); // M2
D cd3 = cd1 + cd2; // M1 + M2
D cd4 = cd3 + cd1; // M1 + M2 + M1
D cd5 = cd4 – cd1; // M1 + M2
}
}
4.委托的調用
直接使用委托實例名和一組符合要求的參數可以調用該委托。
調用一個委托實例,就是依次調用其調用列表中的方法。使用的是同一組參數。
委托實例可以多次出現在一個調用列表中。這種情況下,它每出現一次,就會被調用一次。從這樣的調用列表中移除委托,實際上移除的是調用列表中最后出現的那個委托實例。
5.匿名方法
匿名方法是指初始化委托時內聯申明的方法。
6.委托程序實例
using System;
namespace delegateExam
{
/// <summary>
/// Class1 的摘要說明。
/// </summary>
delegate void TestDelegate(); //在名稱空間下申明委托
class Class1
{
/// <summary>
/// 應用程序的主入口點。
/// </summary>
TestDelegate TD; //申明委托類型的字段
[STAThread]
static void Main(string[] args)
{
//
// TODO: 在此處添加代碼以啟動應用程序
//
TestDelegate TD1 = new TestDelegate(Class1.MethodA); //在方法中申明委托實例並用靜態方法初始化
TestDelegate TD2 = new TestDelegate(test.method2); //在方法中申明委托實例並用靜態方法初始化
Class1 aClass1 = new Class1();
test aTest = new test();
TD2 += new TestDelegate(aClass1.MethodB); //加上實例方法
TD1 += new TestDelegate(aTest.method1); //加上實例方法
Console.WriteLine("調用委托TD1...");
TD1();
Console.WriteLine("調用委托TD2...");
TD2();
TD1 += TD2; //合並兩個委托的調用列表
Console.WriteLine("調用委托TD1和TD2...");
TD1();
TD1 -= TD2;
Console.WriteLine("移除TD2..."); //移除委托TD2
TD1();
TD1 += delegate { Console.WriteLine("添加匿名方法..."); };//添加匿名方法
TD1();
Console.WriteLine("賦值給委托類型的字段...");
aClass1.TD = TD2; //給字段賦值
aClass1.TD();
Console.ReadLine();
}
static void MethodA()
{
Console.WriteLine("類Class1的靜態方法MethodA.");
}
public void MethodB()
{
Console.WriteLine("類Class1實例方法MethodB.");
}
}
class test
{
public void method1()
{
Console.WriteLine("類test的實例方法method1.");
}
public static void method2()
{
Console.WriteLine("類test的靜態方法method2.");
}
}
}
7.事件定義
事件也是類的成員之一,也是一個特殊的委托類型。相當於一個類型為委托的字段或屬性;
事件是當發生某些事情時,類向該類的使用者(其他類)提供通知的一種方法。
8.事件的申明
C#中申明事件的格式之一為:
<事件修飾> event <委托類型> <事件名稱>;
事件修飾可以為:new,public,protected,internal,private,Static,virtual,Sealed,override,abstract,extern
例如:
public event ChangedEventHandler Changed;
這里,ChangedEventHandler是委托類型。必須事先申明,且必須至少具有與事件本身一樣的可視性。 如:
public delegate void ChangedEventHandler(object sender, EventArgs e);
聲明事件的方法與聲明委托類型的字段類似,只是關鍵字 event 在事件聲明前面,在修飾符后面。事件通常被聲明為公共事件,但允許任意可訪問修飾符。
9.事件的調用
在定義事件的類里面,可以像使用字段那樣使用事件(檢查其值和給其賦值);
在發生需要通知客戶類的事情時,調用事件。由於事件本質上是委托類型,調用事件與調用委托一樣。寫法為:
<事件名>([<實參表>]);
例如:If (Changed != null) Changed(this,e);
10.事件訂閱與移除
在定義事件的類外部,只能將委托實例添加到事件的調用列表中或從中移出。寫法同對委托的操作。例如:
List.Changed += new ChangedEventHandler(ListChanged);
或
List.Changed -= new ChangedEventHandler(ListChanged);
重要換句話說,在類外部不可以調用事件,或像使用變量一樣使用事件
這就是事件不同於委托的地方,也是不能完全用委托代替事件的原因。
11.事件程序實例
using System;
//下面的程序展示了標准委托的定義、事件的定義、事件處理方法的定義及他們的綜合應用
//程序將列出0到100之間的所有偶數
namespace eventExam
{
//定義委托類型
delegate void EvenNumberHandler(object sender, OnEvenNumberEventArgs args);
//定義參數類
public class OnEvenNumberEventArgs : EventArgs
{
private int EvenNumber; //存放偶數的字段
public OnEvenNumberEventArgs(int evenNumber) //構造方法
{
this.EvenNumber = evenNumber;
}
public int Number //返回當前偶數的只讀屬性
{ get { return EvenNumber; } }
}
//包含有事件的類
class Counter
{
public event EvenNumberHandler OnEvenNumber; //定義事件
public Counter()
{
OnEvenNumber = null; //在構造方法中,給事件賦初值。只有在定義事件的類中可以這樣。
}
//觸發事件的方法
public void CountTo100()
{
int CurrentNumber;
for(CurrentNumber=0;CurrentNumber<=100;CurrentNumber++)
{
if(CurrentNumber % 2 == 0) //如果為偶數
{
if (OnEvenNumber != null) //如果事件的調用列表不是空的
{
OnEvenNumberEventArgs theArgs = new OnEvenNumberEventArgs(CurrentNumber);
OnEvenNumber(this, theArgs); //觸發事件(其實就是調用委托)
}
}
}
}
}
//對事件進行處理的類
class EvenNumberHandlerClass
{
//對事件進行處理的方法
public void EvenNumberFound(object sender, OnEvenNumberEventArgs args)
{
Console.Write(args.Number);
Console.Write("; ");
}
}
/// <summary>
/// Class1 的摘要說明。
/// </summary>
class MainClass
{
/// <summary>
/// 應用程序的主入口點。
/// </summary>
[STAThread]
static void Main(string[] args)
{
Counter MyCounter = new Counter(); //申明並初始化事件發生的對象
//申明並初始化事件處理對象
EvenNumberHandlerClass MyEvenNumberHandlerClass = new EvenNumberHandlerClass();
//將事件發生對象的事件安裝上事件處理方法。寫法同對委托變量的處理。
MyCounter.OnEvenNumber += new EvenNumberHandler(MyEvenNumberHandlerClass.EvenNumberFound);
MyCounter.CountTo100();
Console.ReadLine();
}
}
}
12.標准化事件的設計
事件可以是系統定義的委托類型,也可以是自己定義的任何委托類型;
但最好采用標准的委托類型;
標准的事件委托類型有兩個參數。
引發事件的對象(命名為:sender)
包含與事件有關數據的參數對象
注意,第二個參數包含所有事件數據。因此需要自己根據需要定義。定義時必須繼承系統定義的EventArgs類。
事件參數類定義舉例:
public class OnEvenNumberEventArgs : EventArgs
{
private int EvenNumber;
public OnEvenNumberEventArgs(int evenNumber)
{
this.EvenNumber = evenNumber;
}
public int Number
{ get { return EvenNumber; } }
}
13.在程序中定義和使用事件的過程:
1.定義委托類型;
2.定義事件參數類型;
3.在類中定義事件;
4.編寫事件處理方法(必須符合委托要求);
5.用事件處理方法初始化委托實例;
6.將委托實例添加到事件的調用列表;
7.觸發事件。