熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

深入解析C#編程中的事件

2022-06-13   來源: .NET編程 

  一個事件是一個使對象或類可以提供公告的成員用戶可以通過提供事件句柄來為事件添加可執行代碼事件使用事件聲明來聲明
一個事件聲明既可以是一個事件域聲明也可以是事件屬性聲明在每種情況中聲明都可以由屬性集合 new 修飾符 四個訪問修飾符的有效組合 和一個靜態修飾符組成
一個事件聲明的類型必須是一個代表類型 而那個代表類型必須至少同事件本身一樣可訪問
一個事件域聲明與一個聲明了一個或多個代表類型域的域聲明相應在一個事件域聲明中不允許有readonly 修飾符
一個事件屬性聲明與聲明了一個代表類型屬性的屬性聲明相應除了同時包含get訪問符和set訪問符的事件屬性聲明成員名稱和訪問符聲明對於那些屬性聲明來說都是相同的並且不允許包含virtual override和abstract 修飾符
在包含一個事件成員聲明的類或結構的程序文字中事件成員與代表類型的私有域或屬性相關而這個成員可以用於任何允許使用域或屬性的上下文中
如果一個類或結構的程序文字外面包含一個事件成員聲明這個事件成員就只能被作為+= 和 = 操作符 (§的右手操作數使用這些操作符被用來為事件成員附加或去掉事件句柄而這個事件成員的訪問操作符控制操作在其中被執行的上下文
由於+= 和 = 是唯一可以在聲明了事件成員的類型外的事件上使用的操作外部代碼可以為一個事件添加或去掉句柄但是不能通過任何其他途徑獲得或修改基本事件域或事件屬性的數值
在例子中
public delegate void EventHandler(object sender Event e);
public class Button: Control
{
 public event EventHandler Click;
 protected void OnClick(Event e) {
if (Click != null) Click(this e);
 }
 public void Reset() {
Click = null;
 }
}
對使用Button類中的Click事件域沒有限制作為演示的例子這個域可以在代表調用表達式中被檢驗修改和使用類Button中的OnClick方法引起Click事件引起一個事件的概念與調用由事件成員表示的代表正好相同-因此對於引起事件沒有特殊的語言構造注意代表的調用是通過檢查保證代表是非空後才進行的
在類Button的聲明外面成員Click只能被用在+= 和 = 操作符右手邊如下
bClick += new EventHandler();
它把一個代表附加到事件Click的調用列表中並且
bClick = new EventHandler();

  它把一個代表從Click事件的調用列表中刪除
在一個形式為x += y 或 x = y的操作中當x是一個事件成員而引用在包含x的聲明的類型外面發生時操作的結果就是void(在賦值後與x的數值相反)這個規則禁止外部代碼直接檢查事件成員的基本代表

  下面的例子介紹了事件句柄如何附加到上面的類Button的實例中

   public class LoginDialog: Form
{
 Button OkButton;
 Button CancelButton;
 public LoginDialog() {
OkButton = new Button();
OkButtonClick += new EventHandler(OkButtonClick);
CancelButton = new Button();
CancelButtonClick += new EventHandler(CancelButtonClick);
 }
 void OkButtonClick(object sender Event e) {
// Handle OkButtonClick event
 }
 void CancelButtonClick(object sender Event e) {
// Handle CancelButtonClick event
 }
}
這裡構造函數LoginDialog創建了兩個Button實例並且把事件句柄附加到事件Click中
事件成員是典型域就像上面的Button例子中所示在每個事件消耗一個域存儲的情況是不可接受的一個類可以聲明事件屬性來替代事件域並且使用私有機制來存儲基本的代表(設想在某種情況下大多數事件都是未處理的每個事件使用一個域就不能被接受使用屬性而不是域的能力允許開發人員在空間和時間上面取得一個折中方案
在例子中
class Control: Component
{
 // Unique keys for events
 static readonly object mouseDownEventKey = new object();
 static readonly object mouseUpEventKey = new object();
 // Return event handler associated with key
 protected Delegate GetEventHandler(object key) {}
 // Set event handler associated with key
 protected void SetEventHandler(object key Delegate handler) {}
 // MouseDown event property
 public event MouseEventHandler MouseDown {
get {
 return (MouseEventHandler)GetEventHandler(mouseDownEventKey);
}
set {
 SetEventHandler(mouseDownEventKey value);
}
 }
 // MouseUp event property
 public event MouseEventHandler MouseUp {
get {
 return (MouseEventHandler)GetEventHandler(mouseUpEventKey);
}
set {
 SetEventHandler(mouseUpEventKey value);
}
}
}

  類Control為事件提供了一種內部存儲機制方法SetEventHandler用一個key來與代表數值相關而方法GetEventHandler返回與key相關的當前代表大概基本的存儲機制是按照把空代表類型與key相關不會有消耗而設計的因此無句柄的事件不占用存儲空間

實例變量初始化函數
當一個構造函數沒有構造初始化函數或一個形式為base()的構造函數初始化函數構造函數就就隱含的執行被類中聲明的實例域的變量初始化函數指定的初始化這與賦值序列相關這些賦值在直接基類構造函數的隱式調用前在構造函數的入口被直接執行變量初始化函數按照它們在類聲明中出現的文字順序執行
構造函數執行
可以把一個實例變量初始化函數和一個構造函數初始化函數看作是自動插在構造函數主體中的第一條語句前例子

   using SystemCollections;
class A
{
 int x = y = count;
 public A() {
count = ;
 }
 public A(int n) {
count = n;
 }
}
class B: A
{
 double sqrt = MathSqrt();
 ArrayList items = new ArrayList();
 int max;
 public B(): this() {
itemsAdd(default);
 }
 public B(int n): base(n ) {
max = n;
 }
}

  包含了許多變量初始化函數並且也包含了每個形式(base和this)的構造函數初始化函數這個例子與下面介紹的例子相關在那裡每條注釋指明了一個自動插入的語句(自動插入構造函數調用所使用的語法不是有效的至少用來演示這個機制)

   using SystemCollections;
class A
{
 int x y count;
 public A() {
x = ; // Variable initializer
y = ; // Variable initializer
object(); // Invoke object() constructor
count = ;
 }
 public A(int n) {
x = ; // Variable initializer
y = ; // Variable initializer
object(); // Invoke object() constructor
count = n;
 }
}
class B: A
{
 double sqrt;
 ArrayList items;
 int max;
 public B(): this() {
B(); // Invoke B(int) constructor
itemsAdd(default);
 }
 public B(int n): base(n ) {
sqrt = MathSqrt(); // Variable initializer
items = new ArrayList(); // Variable initializer
A(n ); // Invoke A(int) constructor
max = n;
 }
}

  注意變量初始化函數被轉換為賦值語句並且那個賦值語句在對基類構造函數調用前執行這個順序確保了所有實例域在任何訪問實例的語句執行前被它們的變量初始化函數初始化例如

   class A
{
 public A() {
PrintFields();
 }
 public virtual void PrintFields() {}
}
class B: A
{
 int x = ;
 int y;
 public B() {
y = ;
 }
 public override void PrintFields() {
ConsoleWriteLine(x = {} y = {} x y);
 }
}

  當new B() 被用來創建B的實例時產生下面的輸出
x = y =

  因為變量初始化函數在基類構造函數被調用前執行所以x的數值是可是y的數值是(int的默認數值)這是因為對y的賦值直到基類構造函數返回才被執行
默認構造函數
如果一個類不包含任何構造函數聲明就會自動提供一個默認的構造函數默認的構造函數通常是下面的形式

   public C(): base() {}

  這裡C是類的名稱默認構造函數完全調用直接基類的無參數構造函數如果直接基類中沒有可訪問的無參數構造函數就會發生錯誤在例子中

   class Message
{
 object sender;
 string text;
}

  因為類不包含構造函數聲明所以提供了一個默認構造函數因此這個例子正好等同於

   class Message
{
 object sender;
 string text;
 public Message(): base() {}
}

  私有構造函數
當一個類只聲明了私有的構造函數時其他類就不能從這個類派生或創建類的實例私有構造函數通常用在只包含靜態成員的類中例如

  public class Trig
{
private Trig() {} // Prevent instantiation
public const double PI = ;
public static double Sin(double x) {}
public static double Cos(double x) {}
public static double Tan(double x) {}
}

Trig 類提供了一組相關的方法和常數但沒有被例示因此它聲明了一個單獨的私有構造函數注意至少要必須聲明一個私有構造函數來避免自動生成默認的構造函數(它通常有公共的訪問性)
可選的構造函數參數
構造函數的this() 形式通常用於與實現可選的構造函數參數的關聯上在這個例子中
class Text
{
 public Text(): this( null) {}
 public Text(int x int y): this(x y null) {}
 public Text(int x int y string s) {
// Actual constructor implementation
 }
}前兩個構造函數只是為丟失的參數提供了默認的數值兩個都使用了一個this()構造函數的初始化函數來調用第三個構造函數它實際上做了對新實例進行初始化的工作效果是那些可選的構造函數參數
Text t = new Text(); // Same as Text( null)
Text t = new Text( ); // Same as Text( null)
Text t = new Text( Hello);
析構函數
析構函數是一個實現破壞一個類的實例的行為的成員析構函數使用析構函數聲明來聲明
一個析構函數聲明的標識符必須為聲明析構函數的類命名如果指定了任何其他名稱就會發生一個錯誤
析構函數聲明的主體指定了為了對類的新實例進行初始化而執行的語句這於一個有void返回類型的實例方法的主體相關
例子
class Test
{
 static void Main() {
AF();
BF();
 }
}
class A
{
 static A() {
ConsoleWriteLine(Init A);
 }
 public static void F() {
ConsoleWriteLine(AF);
 }
}
class B
{
 static B() {
ConsoleWriteLine(Init B);
 }
 public static void F() {
ConsoleWriteLine(BF);
 }
}
會產生或者是下面的輸出
Init A
AF
Init B
BF
或者是下面的輸出
Init B
Init A
AF
BF


From:http://tw.wingwit.com/Article/program/net/201311/12463.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.