一個(gè)事件是一個(gè)使對(duì)象或類可以提供公告的成員。用戶可以通過(guò)提供事件句柄來(lái)為事件添加可執(zhí)行代碼。事件使用事件聲明來(lái)聲明:
一個(gè)事件聲明既可以是一個(gè)事件域聲明也可以是事件屬性聲明。在每種情況中,聲明都可以由屬性集合, new 修飾符, 四個(gè)訪問(wèn)修飾符的有效組合 和一個(gè)靜態(tài)修飾符組成。
一個(gè)事件聲明的類型必須是一個(gè)代表類型, 而那個(gè)代表類型必須至少同事件本身一樣可訪問(wèn)。
一個(gè)事件域聲明與一個(gè)聲明了一個(gè)或多個(gè)代表類型域的域聲明相應(yīng)。在一個(gè)事件域聲明中不允許有readonly 修飾符。
一個(gè)事件屬性聲明與聲明了一個(gè)代表類型屬性的屬性聲明相應(yīng)。除了同時(shí)包含get訪問(wèn)符和set訪問(wèn)符的事件屬性聲明,成員名稱和訪問(wèn)符聲明對(duì)于那些屬性聲明來(lái)說(shuō)都是相同的,并且不允許包含virtual、 override和abstract 修飾符。
在包含一個(gè)事件成員聲明的類或結(jié)構(gòu)的程序文字中,事件成員與代表類型的私有域或?qū)傩韵嚓P(guān),而這個(gè)成員可以用于任何允許使用域或?qū)傩缘纳舷挛闹小?/P>
如果一個(gè)類或結(jié)構(gòu)的程序文字外面包含一個(gè)事件成員聲明,這個(gè)事件成員就只能被作為+= 和 -= 操作符 (§的右手操作數(shù)使用。這些操作符被用來(lái)為事件成員附加或去掉事件句柄,而這個(gè)事件成員的訪問(wèn)操作符控制操作在其中被執(zhí)行的上下文。
由于+= 和 -= 是唯一可以在聲明了事件成員的類型外的事件上使用的操作,外部代碼可以為一個(gè)事件添加或去掉句柄,但是不能通過(guò)任何其他途徑獲得或修改基本事件域或事件屬性的數(shù)值。
在例子中
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;
}
}
對(duì)使用Button類中的Click事件域沒有限制。作為演示的例子,這個(gè)域可以在代表調(diào)用表達(dá)式中被檢驗(yàn)、修改和使用。類Button中的OnClick方法"引起"Click事件。引起一個(gè)事件的概念與調(diào)用由事件成員表示的代表正好相同-因此,對(duì)于引起事件沒有特殊的語(yǔ)言構(gòu)造。注意代表的調(diào)用是通過(guò)檢查保證代表是非空后才進(jìn)行的。
在類Button的聲明外面,成員Click只能被用在+= 和 -= 操作符右手邊,如下
b.Click += new EventHandler(...);
它把一個(gè)代表附加到事件Click的調(diào)用列表中,并且
b.Click -= new EventHandler(...);
它把一個(gè)代表從Click事件的調(diào)用列表中刪除。
在一個(gè)形式為x += y 或 x -= y的操作中,當(dāng)x是一個(gè)事件成員而引用在包含x的聲明的類型外面發(fā)生時(shí),操作的結(jié)果就是void(在賦值后與x的數(shù)值相反)。這個(gè)規(guī)則禁止外部代碼直接檢查事件成員的基本代表。
下面的例子介紹了事件句柄如何附加到上面的類Button的實(shí)例中:
public class LoginDialog: Form
{
Button OkButton;
Button CancelButton;
public LoginDialog() {
OkButton = new Button(...);
OkButton.Click += new EventHandler(OkButtonClick);
CancelButton = new Button(...);
CancelButton.Click += new EventHandler(CancelButtonClick);
}
void OkButtonClick(object sender, Event e) {
// Handle OkButton.Click event
}
void CancelButtonClick(object sender, Event e) {
// Handle CancelButton.Click event
}
}
這里,構(gòu)造函數(shù)LoginDialog創(chuàng)建了兩個(gè)Button實(shí)例,并且把事件句柄附加到事件Click中。
事件成員是典型域,就像上面的Button例子中所示。在每個(gè)事件消耗一個(gè)域存儲(chǔ)的情況是不可接受的,一個(gè)類可以聲明事件屬性來(lái)替代事件域,并且使用私有機(jī)制來(lái)存儲(chǔ)基本的代表。(設(shè)想在某種情況下,大多數(shù)事件都是未處理的,每個(gè)事件使用一個(gè)域就不能被接受。使用屬性而不是域的能力允許開發(fā)人員在空間和時(shí)間上面取得一個(gè)折中方案。)
在例子中
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為事件提供了一種內(nèi)部存儲(chǔ)機(jī)制。方法SetEventHandler用一個(gè)key來(lái)與代表數(shù)值相關(guān),而方法GetEventHandler返回與key相關(guān)的當(dāng)前代表。大概基本的存儲(chǔ)機(jī)制是按照把空代表類型與key相關(guān)不會(huì)有消耗而設(shè)計(jì)的,因此無(wú)句柄的事件不占用存儲(chǔ)空間。
實(shí)例變量初始化函數(shù)
當(dāng)一個(gè)構(gòu)造函數(shù)沒有構(gòu)造初始化函數(shù)或一個(gè)形式為base(...)的構(gòu)造函數(shù)初始化函數(shù),構(gòu)造函數(shù)就就隱含的執(zhí)行被類中聲明的實(shí)例域的變量初始化函數(shù)指定的初始化。這與賦值序列相關(guān),這些賦值在直接基類構(gòu)造函數(shù)的隱式調(diào)用前,在構(gòu)造函數(shù)的入口被直接執(zhí)行。變量初始化函數(shù)按照它們?cè)陬惵暶髦谐霈F(xiàn)的文字順序執(zhí)行。
構(gòu)造函數(shù)執(zhí)行
可以把一個(gè)實(shí)例變量初始化函數(shù)和一個(gè)構(gòu)造函數(shù)初始化函數(shù),看作是自動(dòng)插在構(gòu)造函數(shù)主體中的第一條語(yǔ)句前。例子
using System.Collections;
class A
{
int x = 1, y = -1, count;
public A() {
count = 0;
}
public A(int n) {
count = n;
}
}
class B: A
{
double sqrt2 = Math.Sqrt(2.0);
ArrayList items = new ArrayList(100);
int max;
public B(): this(100) {
items.Add("default");
}
public B(int n): base(n - 1) {
max = n;
}
}
包含了許多變量初始化函數(shù),并且也包含了每個(gè)形式(base和this)的構(gòu)造函數(shù)初始化函數(shù)。這個(gè)例子與下面介紹的例子相關(guān),在那里,每條注釋指明了一個(gè)自動(dòng)插入的語(yǔ)句(自動(dòng)插入構(gòu)造函數(shù)調(diào)用所使用的語(yǔ)法不是有效的,至少用來(lái)演示這個(gè)機(jī)制)。
using System.Collections;
class A
{
int x, y, count;
public A() {
x = 1; // Variable initializer
y = -1; // Variable initializer
object(); // Invoke object() constructor
count = 0;
}
public A(int n) {
x = 1; // Variable initializer
y = -1; // Variable initializer
object(); // Invoke object() constructor
count = n;
}
}
class B: A
{
double sqrt2;
ArrayList items;
int max;
public B(): this(100) {
B(100); // Invoke B(int) constructor
items.Add("default");
}
public B(int n): base(n - 1) {
sqrt2 = Math.Sqrt(2.0); // Variable initializer
items = new ArrayList(100); // Variable initializer
A(n - 1); // Invoke A(int) constructor
max = n;
}
}
注意變量初始化函數(shù)被轉(zhuǎn)換為賦值語(yǔ)句,并且那個(gè)賦值語(yǔ)句在對(duì)基類構(gòu)造函數(shù)調(diào)用前執(zhí)行。這個(gè)順序確保了所有實(shí)例域在任何訪問(wèn)實(shí)例的語(yǔ)句執(zhí)行前,被它們的變量初始化函數(shù)初始化。例如:
class A
{
public A() {
PrintFields();
}
public virtual void PrintFields() {}
}
class B: A
{
int x = 1;
int y;
public B() {
y = -1;
}
public override void PrintFields() {
Console.WriteLine("x = {0}, y = {1}", x, y);
}
}
當(dāng)new B() 被用來(lái)創(chuàng)建B的實(shí)例時(shí),產(chǎn)生下面的輸出:
x = 1, y = 0
因?yàn)樽兞砍跏蓟瘮?shù)在基類構(gòu)造函數(shù)被調(diào)用前執(zhí)行,所以x的數(shù)值是1。可是,y的數(shù)值是0(int的默認(rèn)數(shù)值),這是因?yàn)閷?duì)y的賦值直到基類構(gòu)造函數(shù)返回才被執(zhí)行。
默認(rèn)構(gòu)造函數(shù)
如果一個(gè)類不包含任何構(gòu)造函數(shù)聲明,就會(huì)自動(dòng)提供一個(gè)默認(rèn)的構(gòu)造函數(shù)。默認(rèn)的構(gòu)造函數(shù)通常是下面的形式
public C(): base() {}
這里C是類的名稱。默認(rèn)構(gòu)造函數(shù)完全調(diào)用直接基類的無(wú)參數(shù)構(gòu)造函數(shù)。如果直接基類中沒有可訪問(wèn)的無(wú)參數(shù)構(gòu)造函數(shù),就會(huì)發(fā)生錯(cuò)誤。在例子中
class Message
{
object sender;
string text;
}
因?yàn)轭惒话瑯?gòu)造函數(shù)聲明,所以提供了一個(gè)默認(rèn)構(gòu)造函數(shù)。因此,這個(gè)例子正好等同于
class Message
{
object sender;
string text;
public Message(): base() {}
}
私有構(gòu)造函數(shù)
當(dāng)一個(gè)類只聲明了私有的構(gòu)造函數(shù)時(shí),其他類就不能從這個(gè)類派生或創(chuàng)建類的實(shí)例。私有構(gòu)造函數(shù)通常用在只包含靜態(tài)成員的類中。例如:
public class Trig
{
private Trig() {} // Prevent instantiation
public const double PI = 3.14159265358979323846;
public static double Sin(double x) {...}
public static double Cos(double x) {...}
public static double Tan(double x) {...}
}
Trig 類提供了一組相關(guān)的方法和常數(shù),但沒有被例示。因此,它聲明了一個(gè)單獨(dú)的私有構(gòu)造函數(shù)。注意至少要必須聲明一個(gè)私有構(gòu)造函數(shù)來(lái)避免自動(dòng)生成默認(rèn)的構(gòu)造函數(shù)(它通常有公共的訪問(wèn)性)。
可選的構(gòu)造函數(shù)參數(shù)
構(gòu)造函數(shù)的this(...) 形式通常用于與實(shí)現(xiàn)可選的構(gòu)造函數(shù)參數(shù)的關(guān)聯(lián)上。在這個(gè)例子中
class Text
{
public Text(): this(0, 0, null) {}
public Text(int x, int y): this(x, y, null) {}
public Text(int x, int y, string s) {
// Actual constructor implementation
}
}
前兩個(gè)構(gòu)造函數(shù)只是為丟失的參數(shù)提供了默認(rèn)的數(shù)值。兩個(gè)都使用了一個(gè)this(...)構(gòu)造函數(shù)的初始化函數(shù)來(lái)調(diào)用第三個(gè)構(gòu)造函數(shù),它實(shí)際上做了對(duì)新實(shí)例進(jìn)行初始化的工作。效果是那些可選的構(gòu)造函數(shù)參數(shù):
Text t1 = new Text(); // Same as Text(0, 0, null)
Text t2 = new Text(5, 10); // Same as Text(5, 10, null)
Text t3 = new Text(5, 20, "Hello");
析構(gòu)函數(shù)
析構(gòu)函數(shù)是一個(gè)實(shí)現(xiàn)破壞一個(gè)類的實(shí)例的行為的成員。析構(gòu)函數(shù)使用析構(gòu)函數(shù)聲明來(lái)聲明:
一個(gè)析構(gòu)函數(shù)聲明的標(biāo)識(shí)符必須為聲明析構(gòu)函數(shù)的類命名,如果指定了任何其他名稱,就會(huì)發(fā)生一個(gè)錯(cuò)誤。
析構(gòu)函數(shù)聲明的主體指定了為了對(duì)類的新實(shí)例進(jìn)行初始化而執(zhí)行的語(yǔ)句。這于一個(gè)有void返回類型的實(shí)例方法的主體相關(guān)。
例子
class Test
{
static void Main() {
A.F();
B.F();
}
}
class A
{
static A() {
Console.WriteLine("Init A");
}
public static void F() {
Console.WriteLine("A.F");
}
}
class B
{
static B() {
Console.WriteLine("Init B");
}
public static void F() {
Console.WriteLine("B.F");
}
}
會(huì)產(chǎn)生或者是下面的輸出:
Init A
A.F
Init B
B.F
或者是下面的輸出:
Init B
Init A
A.F
B.F
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注