C#笔记(二):委托与事件
本文內(nèi)容:
1. ?委托定義
2. ?申明委托
3.?委托的實(shí)例化
4.?委托的調(diào)用
5.?匿名方法
6. 委托程序?qū)嵗?
7.?事件定義
8.?事件的申明
9.?事件的調(diào)用
10.?事件訂閱與移除
11.?事件程序?qū)嵗?
12.?標(biāo)準(zhǔn)化事件的設(shè)計(jì)
?
?
?
1. 委托定義
委托(Delegate)是C#或者.NET中表示強(qiáng)類型方法的特殊類型。比較接近于C語(yǔ)言中的函數(shù)指針。(指向函數(shù)入口地址的數(shù)據(jù)類型)。讀到這里說下C語(yǔ)言的兩個(gè)概念:指針函數(shù)和函數(shù)指針。
指針函數(shù)是指帶指針的函數(shù),即本質(zhì)是一個(gè)函數(shù)。函數(shù)返回類型是某一類型的指針。
int *fun(x);?
函數(shù)指針是指向函數(shù)的指針變量,即本質(zhì)是一個(gè)指針變量。
int (*f) (int x); /* 聲明一個(gè)函數(shù)指針 */f=function; /* 將function函數(shù)的首地址賦給指針f */
?
因此由上面可以看出,委托其實(shí)是一種封裝好的函數(shù)指針。
?
2. 申明委托
申明委托使用delegate關(guān)鍵字,語(yǔ)法是:
[<修飾符>]?delegate?<返回類型>?<委托名>?([<形參表>])
修飾符:new、public、protected、internal、private。如果在namespace下,只能用public,internal 。
返回類型:與方法的返回類型相同
委托名:與其他類型的標(biāo)識(shí)符要求相同,常用handler作后綴
形參表:與方法的形參表的要求相同
舉例:delegate void D1(int i, double d);
?
?
3.委托的實(shí)例化
由于委托類似方法指針,申明委托時(shí)給出的返回類型和形參表,實(shí)際上是指該委托所能指向或者封裝的方法的返回類型和參數(shù)表。
凡是與委托的返回類型和形參表相同的方法,不管是靜態(tài)的還是實(shí)例的,稱為與委托的類型兼容,都可以被該委托的實(shí)例封裝。
委托類型定義后,可以像其他類型一樣申明該類型的變量,并對(duì)變量實(shí)例化。
?
對(duì)委托進(jìn)行實(shí)例化的寫法為:
?? new <委托類型名> (<表達(dá)式>)
?? 這里,表達(dá)式可以是一個(gè)兼容的靜態(tài)方法、實(shí)例方法、或者一個(gè)委托實(shí)例。
例如:
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); // 用靜態(tài)方法
C t = new C(); // 實(shí)例化有關(guān)類
D cd2 = new D(t.M2); // 用實(shí)例方法
D cd3 = new D(cd2); // 用一個(gè)委托實(shí)例
}
}
從某個(gè)方法創(chuàng)建一個(gè)委托實(shí)例時(shí),該委托實(shí)例將封裝此方法,此時(shí),它的調(diào)用列表只包含一個(gè)進(jìn)入點(diǎn)。 可用 +和 += 運(yùn)算符組合委托實(shí)例,用 -和 -= 運(yùn)算符將一個(gè)委托從委托組合中移除。
當(dāng)組合兩個(gè)或多個(gè)非空委托實(shí)例時(shí),它們的調(diào)用列表被連接在一起(按照左操作數(shù)在先、右操作數(shù)在后的順序)以組成一個(gè)新的調(diào)用列表,它包含了兩個(gè)或更多個(gè)“進(jìn)入點(diǎn)”。
例如:委托的組合及調(diào)用列表
?
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.委托的調(diào)用
直接使用委托實(shí)例名和一組符合要求的參數(shù)可以調(diào)用該委托。
調(diào)用一個(gè)委托實(shí)例,就是依次調(diào)用其調(diào)用列表中的方法。使用的是同一組參數(shù)。
委托實(shí)例可以多次出現(xiàn)在一個(gè)調(diào)用列表中。這種情況下,它每出現(xiàn)一次,就會(huì)被調(diào)用一次。從這樣的調(diào)用列表中移除委托,實(shí)際上移除的是調(diào)用列表中最后出現(xiàn)的那個(gè)委托實(shí)例。
?
5.匿名方法
匿名方法是指初始化委托時(shí)內(nèi)聯(lián)申明的方法。
?
6.委托程序?qū)嵗?
using System;namespace delegateExam
{
/// <summary>
/// Class1 的摘要說明。
/// </summary>
delegate void TestDelegate(); //在名稱空間下申明委托
class Class1
{
/// <summary>
/// 應(yīng)用程序的主入口點(diǎn)。
/// </summary>
TestDelegate TD; //申明委托類型的字段
[STAThread]
static void Main(string[] args)
{
//
// TODO: 在此處添加代碼以啟動(dòng)應(yīng)用程序
//
TestDelegate TD1 = new TestDelegate(Class1.MethodA); //在方法中申明委托實(shí)例并用靜態(tài)方法初始化
TestDelegate TD2 = new TestDelegate(test.method2); //在方法中申明委托實(shí)例并用靜態(tài)方法初始化
Class1 aClass1 = new Class1();
test aTest = new test();
TD2 += new TestDelegate(aClass1.MethodB); //加上實(shí)例方法
TD1 += new TestDelegate(aTest.method1); //加上實(shí)例方法
Console.WriteLine("調(diào)用委托TD1...");
TD1();
Console.WriteLine("調(diào)用委托TD2...");
TD2();
TD1 += TD2; //合并兩個(gè)委托的調(diào)用列表
Console.WriteLine("調(diào)用委托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的靜態(tài)方法MethodA.");
}
public void MethodB()
{
Console.WriteLine("類Class1實(shí)例方法MethodB.");
}
}
class test
{
public void method1()
{
Console.WriteLine("類test的實(shí)例方法method1.");
}
public static void method2()
{
Console.WriteLine("類test的靜態(tài)方法method2.");
}
}
}
?
7.事件定義
事件也是類的成員之一,也是一個(gè)特殊的委托類型。相當(dāng)于一個(gè)類型為委托的字段或?qū)傩?#xff1b;
事件是當(dāng)發(fā)生某些事情時(shí),類向該類的使用者(其他類)提供通知的一種方法。
?
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);聲明事件的方法與聲明委托類型的字段類似,只是關(guān)鍵字 event 在事件聲明前面,在修飾符后面。事件通常被聲明為公共事件,但允許任意可訪問修飾符。
?
9.事件的調(diào)用
在定義事件的類里面,可以像使用字段那樣使用事件(檢查其值和給其賦值);
在發(fā)生需要通知客戶類的事情時(shí),調(diào)用事件。由于事件本質(zhì)上是委托類型,調(diào)用事件與調(diào)用委托一樣。寫法為:
??? <事件名>([<實(shí)參表>]);
例如:If (Changed != null)? Changed(this,e);
?
10.事件訂閱與移除
在定義事件的類外部,只能將委托實(shí)例添加到事件的調(diào)用列表中或從中移出。寫法同對(duì)委托的操作。例如:
???????? List.Changed += new ChangedEventHandler(ListChanged);
或????
???????? List.Changed -= new ChangedEventHandler(ListChanged);
重要換句話說,在類外部不可以調(diào)用事件,或像使用變量一樣使用事件
這就是事件不同于委托的地方,也是不能完全用委托代替事件的原因。
11.事件程序?qū)嵗?
using System;//下面的程序展示了標(biāo)準(zhǔn)委托的定義、事件的定義、事件處理方法的定義及他們的綜合應(yīng)用
//程序?qū)⒘谐?到100之間的所有偶數(shù)
namespace eventExam
{
//定義委托類型
delegate void EvenNumberHandler(object sender, OnEvenNumberEventArgs args);
//定義參數(shù)類
public class OnEvenNumberEventArgs : EventArgs
{
private int EvenNumber; //存放偶數(shù)的字段
public OnEvenNumberEventArgs(int evenNumber) //構(gòu)造方法
{
this.EvenNumber = evenNumber;
}
public int Number //返回當(dāng)前偶數(shù)的只讀屬性
{ get { return EvenNumber; } }
}
//包含有事件的類
class Counter
{
public event EvenNumberHandler OnEvenNumber; //定義事件
public Counter()
{
OnEvenNumber = null; //在構(gòu)造方法中,給事件賦初值。只有在定義事件的類中可以這樣。
}
//觸發(fā)事件的方法
public void CountTo100()
{
int CurrentNumber;
for(CurrentNumber=0;CurrentNumber<=100;CurrentNumber++)
{
if(CurrentNumber % 2 == 0) //如果為偶數(shù)
{
if (OnEvenNumber != null) //如果事件的調(diào)用列表不是空的
{
OnEvenNumberEventArgs theArgs = new OnEvenNumberEventArgs(CurrentNumber);
OnEvenNumber(this, theArgs); //觸發(fā)事件(其實(shí)就是調(diào)用委托)
}
}
}
}
}
//對(duì)事件進(jìn)行處理的類
class EvenNumberHandlerClass
{
//對(duì)事件進(jìn)行處理的方法
public void EvenNumberFound(object sender, OnEvenNumberEventArgs args)
{
Console.Write(args.Number);
Console.Write("; ");
}
}
/// <summary>
/// Class1 的摘要說明。
/// </summary>
class MainClass
{
/// <summary>
/// 應(yīng)用程序的主入口點(diǎn)。
/// </summary>
[STAThread]
static void Main(string[] args)
{
Counter MyCounter = new Counter(); //申明并初始化事件發(fā)生的對(duì)象
//申明并初始化事件處理對(duì)象
EvenNumberHandlerClass MyEvenNumberHandlerClass = new EvenNumberHandlerClass();
//將事件發(fā)生對(duì)象的事件安裝上事件處理方法。寫法同對(duì)委托變量的處理。
MyCounter.OnEvenNumber += new EvenNumberHandler(MyEvenNumberHandlerClass.EvenNumberFound);
MyCounter.CountTo100();
Console.ReadLine();
}
}
}
12.標(biāo)準(zhǔn)化事件的設(shè)計(jì)
事件可以是系統(tǒng)定義的委托類型,也可以是自己定義的任何委托類型;
但最好采用標(biāo)準(zhǔn)的委托類型;
標(biāo)準(zhǔn)的事件委托類型有兩個(gè)參數(shù)。
引發(fā)事件的對(duì)象(命名為:sender)
包含與事件有關(guān)數(shù)據(jù)的參數(shù)對(duì)象
???????? 注意,第二個(gè)參數(shù)包含所有事件數(shù)據(jù)。因此需要自己根據(jù)需要定義。定義時(shí)必須繼承系統(tǒng)定義的EventArgs類。
?
事件參數(shù)類定義舉例:
public class OnEvenNumberEventArgs : EventArgs{
private int EvenNumber;
public OnEvenNumberEventArgs(int evenNumber)
{
this.EvenNumber = evenNumber;
}
public int Number
{ get { return EvenNumber; } }
}
13.在程序中定義和使用事件的過程:
1.定義委托類型;
2.定義事件參數(shù)類型;
3.在類中定義事件;
4.編寫事件處理方法(必須符合委托要求);
5.用事件處理方法初始化委托實(shí)例;
6.將委托實(shí)例添加到事件的調(diào)用列表;
7.觸發(fā)事件。
?
作者: ForEvErNoME出處: http://www.cnblogs.com/ForEvErNoME/
歡迎轉(zhuǎn)載或分享,但請(qǐng)務(wù)必聲明文章出處。如果文章對(duì)您有幫助,希望你能 推薦 或 關(guān)注
轉(zhuǎn)載于:https://www.cnblogs.com/ForEvErNoME/archive/2012/02/21/2362020.html
總結(jié)
以上是生活随笔為你收集整理的C#笔记(二):委托与事件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 十四、List,Set,Collecti
- 下一篇: OmniMarkupPreview 插件