C#筆記(二):委托與事件


本文內容:

  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.觸發事件。

 

作者: ForEvErNoME
出處: http://www.cnblogs.com/ForEvErNoME/
歡迎轉載或分享,但請務必聲明文章出處。如果文章對您有幫助,希望你能 推薦關注
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM