更新:2007 年 11 月
C# 提供了一些實用的語言功能,例如索引子 (Indexer)、屬性 (Attribute) 和委派 (Delegate),這些功能提供了進階的程式設計技巧。
索引子
索引子提供了與陣列相同的方式來存取 class 或 struct。例如,您可以有一個類別代表公司裡的單一部門。這個類別可能包含此部門中所有員工的姓名,而且索引子可讓您以下列方式存取這些姓名:
sales[0] = "Nikki";
sales[1] = "Becky";
例如,在類別定義中使用下列簽章定義屬性,會啟用索引子:
public string this [int index] //indexer
接著,就像您會為一般屬性所做的一樣,提供 get 和 set 方法,這些存取子 (Accessor) 會用來指定使用索引子時內部成員的參考對象。
在下列簡單範例中,將建立一個名為 Department 的類別,此類別使用索引子來存取該部門中的員工,在內部是以字串陣列表示:
public class Department
{
private string name;
private const int MAX_EMPLOYEES = 10;
private string[] employees = new string[MAX_EMPLOYEES]; //employee array
public Department(string departmentName) //constructor
{
name = departmentName;
}
public string this [int index] //indexer
{
get
{
if (index >= 0 && index < MAX_EMPLOYEES)
{
return employees[index];
}
else
{
throw new System.IndexOutOfRangeException();
}
}
set
{
if (index >= 0 && index < MAX_EMPLOYEES)
{
employees[index] = value;
}
else
{
throw new System.IndexOutOfRangeException();
}
}
}
// code for the rest of the class...
}
然後您便能夠建立這個類別的執行個體並且存取它,如下列程式碼範例所示:
class TestDepartment
{
static void Main()
{
Department sales = new Department("Sales");
sales[0] = "Nikki";
sales[1] = "Becky";
System.Console.WriteLine("The sales team is {0} and {1}", sales[0], sales[1]);
}
}
輸出如下:
The sales team is Nikki and Becky
如需詳細資訊,請參閱索引子 (C# 程式設計手冊)。
屬性
C# 提供了一種稱為「屬性」(Attribute) 的機制,可用來加入與型別有關的宣告式資訊。屬性有點類似 Java 內的附註概念。關於型別的額外資訊是放置在宣告式標記之內,此標記位於型別定義之前。下列範例說明如何使用 .NET Framework 屬性來裝飾類別或方法。
在下列範例中,藉由加入 WebMethodAttribute 屬性,將 GetTime 方法標記為 XML Web 服務。
public class Utilities : System.Web.Services.WebService
{
[System.Web.Services.WebMethod] // Attribute
public string GetTime()
{
return System.DateTime.Now.ToShortTimeString();
}
}
加入 WebMethod 屬性可讓 .NET Framework 自動負責執行呼叫這個函式時所需的 XML/SOAP 交換。呼叫這個 Web 服務可擷取下列值:
<?xml version="1.0" encoding="utf-8" ?>
<string xmlns="http://tempuri.org/">7:26 PM</string>
在下列範例中,藉由加入 SerializableAttribute 屬性,將 Employee 類別標記為可序列化。由於 Salary 欄位標記為公用,因此當您使用 NonSerializedAttribute 屬性來標記這個欄位時,它將不會序列化。
[System.Serializable()]
public class Employee
{
public int ID;
public string Name;
[System.NonSerialized()] public int Salary;
}
如需詳細資訊,請參閱建立自訂屬性 (C# 程式設計手冊)。
委派
例如 C++、Pascal 等程式語言支援函式指標的概念,函式指標可讓您選擇要在執行階段呼叫的函式。
Java 不提供具有函式指標功能的建構,但是 C# 提供這樣的建構。委派執行個體使用 Delegate 類別來封裝方法,此方法為可呼叫的實體 (Entity)。
對於執行個體方法 (Instance Method) 而言,委派是由包含類別的執行個體以及執行個體上的方法所組成。對於靜態方法而言,可呼叫的實體是由類別以及類別上的靜態方法所組成。因此,委派可用來叫用任何物件的函式,而且委派具有物件導向、型別安全 (Type-Safe) 和安全 (Secure)的特性。
定義並使用委派共有三個步驟:
宣告
執行個體化
引動過程
請使用下列語法來宣告委派:
delegate void Del1();
接著,這個委派可用來參考任何傳回虛值 (Void) 且不帶引數的函式。
同樣地,您會使用下列語法,為含有字串參數且傳回長整數 (Long) 的函式建立委派:
delegate long Del2(string s);
接著,您可將這個委派指派給任何具有這個簽章的方法,如下所示:
Del2 d; // declare the delegate variable
d = DoWork; // set the delegate to refer to the DoWork method
DoWork 的簽章為:
public static long DoWork(string name)
重新指派委派
Delegate 物件是不變的,也就是說,符合的簽章一旦設定後就無法變更。不過,您也可以指向其他方法,只要這兩個方法具有相同的簽章即可。在此範例中,您重新指派 d 給新的委派物件,所以接著 d 會叫用 DoMoreWork 方法。如果 DoWork 和 DoMoreWork 擁有相同的簽章,那麼您只能這樣做。
Del2 d; // declare the delegate variable
d = DoWork; // set the delegate to refer to the DoWork method
d = DoMoreWork; // reassign the delegate to refer to the DoMoreWork method
叫用委派
叫用委派是十分簡單的作業。只需要使用委派變數的名稱來替代方法名稱。這個程式碼會使用 11 和 22 兩個值叫用 Add 方法,並傳回指派給變數 sum 的長整數結果:
Del operation; // declare the delegate variable
operation = Add; // set the delegate to refer to the Add method
long sum = operation(11, 22); // invoke the delegate
下列程式碼說明委派的建立、執行個體化和引動過程:
public class MathClass
{
public static long Add(int i, int j) // static
{
return (i + j);
}
public static long Multiply (int i, int j) // static
{
return (i * j);
}
}
class TestMathClass
{
delegate long Del(int i, int j); // declare the delegate type
static void Main()
{
Del operation; // declare the delegate variable
operation = MathClass.Add; // set the delegate to refer to the Add method
long sum = operation(11, 22); // use the delegate to call the Add method
operation = MathClass.Multiply; // change the delegate to refer to the Multiply method
long product = operation(30, 40); // use the delegate to call the Multiply method
System.Console.WriteLine("11 + 22 = " + sum);
System.Console.WriteLine("30 * 40 = " + product);
}
}
輸出
11 + 22 = 33
30 * 40 = 1200
委派執行個體必須包含物件參考。由於上述範例宣告方法為靜態 (這表示不需指定物件參考),所以不必理會這項要求。但是,如果委派參考執行個體方法,那麼就必須設定物件參考,如下所示:
Del operation; // declare the delegate variable
MathClass m1 = new MathClass(); // declare the MathClass instance
operation = m1.Add; // set the delegate to refer to the Add method
在這個範例中,Add 和 Multiply 是 MathClass 的執行個體方法。如果 MathClass 的方法並非宣告為靜態,您就要使用 MathClass 的執行個體,透過委派叫用它們,如下所示:
public class MathClass
{
public long Add(int i, int j) // not static
{
return (i + j);
}
public long Multiply (int i, int j) // not static
{
return (i * j);
}
}
class TestMathClass
{
delegate long Del(int i, int j); // declare the delegate type
static void Main()
{
Del operation; // declare the delegate variable
MathClass m1 = new MathClass(); // declare the MathClass instance
operation = m1.Add; // set the delegate to refer to the Add method
long sum = operation(11, 22); // use the delegate to call the Add method
operation = m1.Multiply; // change the delegate to refer to the Multiply method
long product = operation(30, 40); // use the delegate to call the Multiply method
System.Console.WriteLine("11 + 22 = " + sum);
System.Console.WriteLine("30 * 40 = " + product);
}
}
輸出
這個範例的輸出與上個範例相同,但上個範例是將方法宣告為靜態。
11 + 22 = 33
30 * 40 = 1200
委派和事件
.NET Framework 在執行事件處理工作時也大量使用委派,例如 Windows 應用程式或 Web 應用程式中的按鈕點選事件。在 Java 中通常是實作自訂接聽程式類別來處理事件,而 C# 開發人員則可善用委派來處理事件。event 是被宣告為具有委派型別的欄位,但不同的是,關鍵字 event 會出現在事件宣告之前。事件通常宣告為 public,但是它可以使用任何存取範圍修飾詞。在下列程式碼示範了 delegate 和 event 的宣告。
// Declare the delegate type:
public delegate void CustomEventHandler(object sender, System.EventArgs e);
// Declare the event variable using the delegate type:
public event CustomEventHandler CustomEvent;
事件委派為多點傳送,這表示它們可以存有對一個以上事件處理方法的參考。委派可藉由維持事件之已註冊事件處理常式的清單,做為引發事件之類別的事件分派者。在下列程式碼示範了如何讓多個函式訂閱某個事件。EventClass 類別包含委派、事件以及叫用該事件的方法。請注意,只能夠從宣告事件的類別中叫用事件。接著,TestEvents 類別便可使用 += 運算子來訂閱事件,以及使用 -= 運算子來取消訂閱。呼叫 InvokeEvent 方法時會引發事件,而且訂閱事件的所有函式也會同步引發,如以下範例所示:
public class EventClass
{
// Declare the delegate type:
public delegate void CustomEventHandler(object sender, System.EventArgs e);
// Declare the event variable using the delegate type:
public event CustomEventHandler CustomEvent;
public void InvokeEvent()
{
// Invoke the event from within the class that declared the event:
CustomEvent(this, System.EventArgs.Empty);
}
}
class TestEvents
{
private static void CodeToRun(object sender, System.EventArgs e)
{
System.Console.WriteLine("CodeToRun is executing");
}
private static void MoreCodeToRun(object sender, System.EventArgs e)
{
System.Console.WriteLine("MoreCodeToRun is executing");
}
static void Main()
{
EventClass ec = new EventClass();
ec.CustomEvent += new EventClass.CustomEventHandler(CodeToRun);
ec.CustomEvent += new EventClass.CustomEventHandler(MoreCodeToRun);
System.Console.WriteLine("First Invocation:");
ec.InvokeEvent();
ec.CustomEvent -= new EventClass.CustomEventHandler(MoreCodeToRun);
System.Console.WriteLine("\nSecond Invocation:");
ec.InvokeEvent();
}
}
輸出
First Invocation:
CodeToRun is executing
MoreCodeToRun is executing
Second Invocation:
CodeToRun is executing