1. 程式人生 > >C#6.0語言規範(十五) 委托

C#6.0語言規範(十五) 委托

順序 構造 oid 完全 底層 有關 形式參數 class parameter

委托啟用其他語言(如C ++,Pascal和Modula)已使用函數指針進行尋址的方案。但是,與C ++函數指針不同,委托是完全面向對象的,與成員函數的C ++指針不同,委托封裝了對象實例和方法。

委托聲明定義了從類派生的類System.Delegate委托實例封裝了一個調用列表,該列表是一個或多個方法的列表,每個方法都被稱為可調用實體。對於實例方法,可調用實體由該實例上的實例和方法組成。對於靜態方法,可調用實體僅包含一個方法。使用適當的參數集調用委托實例會導致使用給定的參數集調用每個委托的可調用實體。

委托實例的一個有趣且有用的屬性是它不知道或不關心它封裝的方法的類; 重要的是這些方法

與委托的類型兼容(委托聲明)。這使得委托完全適合“匿名”調用。

委托聲明

一個delegate_declarationtype_declaration類型聲明,聲明一個新的委托類型)。

 1 delegate_declaration
 2     : attributes? delegate_modifier* delegate return_type
 3       identifier variant_type_parameter_list?
 4       ( formal_parameter_list? ) type_parameter_constraints_clause* 
; 5 ; 6 7 delegate_modifier 8 : new 9 | public 10 | protected 11 | internal 12 | private 13 | delegate_modifier_unsafe 14 ;

同一修飾符在委托聲明中多次出現是編譯時錯誤。

所述new改性劑只允許在另一種類型的,在這種情況下,它指明這種委托隱藏同名一個繼承的成員,如在描述中聲明委托新的改性劑

publicprotectedinternal,和private

修飾符控制委托類型的可訪問性。根據委托聲明發生的上下文,可能不允許某些修飾符(聲明的可訪問性)。

委托的類型名稱是標識符

可選的formal_parameter_list指定委托的參數,return_type指示委托的返回類型。

可選的variant_type_parameter_listVariant類型參數列表)指定委托本身的類型參數。

委托類型的返回類型必須是void或輸出安全(方差安全)。

委托類型的所有形式參數類型必須是輸入安全的。此外,任何outref參數類型也必須是輸出安全的。請註意out,由於底層執行平臺的限制,甚至參數都需要輸入安全。

C#中的委托類型是名稱等價的,在結構上不等同。具體而言,具有相同參數列表和返回類型的兩種不同委托類型被視為不同的委托類型。但是,兩個不同但結構上等效的委托類型的實例可以比較為相等(委托相等運算符)。

 1 delegate int D1(int i, double d);
 2 
 3 class A
 4 {
 5     public static int M1(int a, double b) {...}
 6 }
 7 
 8 class B
 9 {
10     delegate int D2(int c, double d);
11     public static int M1(int f, double g) {...}
12     public static void M2(int k, double l) {...}
13     public static int M3(int g) {...}
14     public static void M4(int g) {...}
15 }

這些方法A.M1B.M1與雙方委托類型兼容D1D2,因為它們具有相同的返回類型和參數列表; 但是,這些委托類型是兩種不同的類型,因此它們不可互換。的方法B.M2B.M3以及B.M4與委托類型不兼容D1D2,因為他們有不同的返回類型或參數列表。

與其他泛型類型聲明一樣,必須提供類型參數以創建構造的委托類型。通過為委托聲明中的每個類型參數替換構造的委托類型的相應類型參數,來創建構造的委托類型的參數類型和返回類型。生成的返回類型和參數類型用於確定哪些方法與構造的委托類型兼容。例如:

1 delegate bool Predicate<T>(T value);
2 
3 class X
4 {
5     static bool F(int i) {...}
6     static bool G(string s) {...}
7 }

該方法X.F與委托類型兼容,Predicate<int>並且該方法X.G與委托類型兼容Predicate<string>

聲明委托類型的唯一方法是通過delegate_declaration委托類型是派生自的類類型System.Delegate委托類型是隱式的sealed,因此不允許從委托類型派生任何類型。也不允許從中派生非委托類類型System.Delegate請註意,System.Delegate它本身不是委托類型; 它是一個類類型,從中派生所有委托類型。

C#為委托實例化和調用提供了特殊語法。除了實例化之外,任何可以應用於類或類實例的操作也可以分別應用於委托類或實例。特別是,可以System.Delegate通過通常的成員訪問語法訪問類型的成員。

由委托實例封裝的方法集稱為調用列表。從單個方法創建委托實例(委托兼容性)時,它封裝該方法,並且其調用列表僅包含一個條目。但是,當組合兩個非null委托實例時,它們的調用列表將按照左操作數和右操作數的順序連接,以形成一個新的調用列表,其中包含兩個或多個條目。

使用二進制+加法運算符)和+=運算符(復合賦值組合委托可以使用二進制-減法運算符)和-=運算符(復合賦值從委托組合中刪除委托可以比較委托的相等性(委托相等運算符)。

以下示例顯示了許多委托的實例化及其相應的調用列表:

 1 delegate void D(int x);
 2 
 3 class C
 4 {
 5     public static void M1(int i) {...}
 6     public static void M2(int i) {...}
 7 
 8 }
 9 
10 class Test
11 {
12     static void Main() {
13         D cd1 = new D(C.M1);      // M1
14         D cd2 = new D(C.M2);      // M2
15         D cd3 = cd1 + cd2;        // M1 + M2
16         D cd4 = cd3 + cd1;        // M1 + M2 + M1
17         D cd5 = cd4 + cd3;        // M1 + M2 + M1 + M1 + M2
18     }
19 
20 }

cd1cd2實例化時,它們都封裝了一個方法。cd3實例化時,它具有兩個方法的調用列表,M1並按M2順序。cd4的調用列表包含M1,, M2M1,按順序。最後cd5的調用列表中包含M1M2M1M1,並M2按此順序。有關組合(以及刪除)委托的更多示例,請參閱委派調用

委派兼容性

的方法或委托M兼容與委托類型D如果以下所有條件都為真:

  • D並且M具有相同數量的參數,並且每個參數與相應的參數D具有相同refout修飾符M
  • 對於每個值參數(帶有no refoutmodifier 的參數),從參數類型到相應的參數類型中存在標識轉換(標識轉換)或隱式引用轉換(隱式引用轉換DM
  • 對於每個refout參數,參數類型in與參數類型D相同M
  • 從返回類型M到返回類型的存在標識或隱式引用轉換D

委托實例化

委托的實例由delegate_creation_expression委托創建表達式)或轉換為委托類型創建。新創建的委托實例然後引用:

  • delegate_creation_expression中引用的靜態方法,或
  • nulldelegate_creation_expression中引用的目標對象(不可以)和實例方法,或
  • 另一位委托。

例如:

 1 delegate void D(int x);
 2 
 3 class C
 4 {
 5     public static void M1(int i) {...}
 6     public void M2(int i) {...}
 7 }
 8 
 9 class Test
10 {
11     static void Main() { 
12         D cd1 = new D(C.M1);        // static method
13         C t = new C();
14         D cd2 = new D(t.M2);        // instance method
15         D cd3 = new D(cd2);        // another delegate
16     }
17 }

實例化後,委托實例始終引用相同的目標對象和方法。請記住,當兩個委托組合在一起,或者一個委托從另一個委托中刪除時,新的委托會產生自己的調用列表; 組合或刪除的委托的調用列表保持不變。

委托調用

C#提供了調用委托的特殊語法。當調用其調用列表包含一個條目的非null委托實例時,它將調用具有相同參數的one方法,並返回與引用方法相同的值。(有關委托調用的詳細信息,請參閱委派調用。)如果在調用此類委托期間發生異常,並且該異常未在調用的方法中捕獲,則在調用的方法中繼續搜索異常catch子句委托,就好像該方法直接調用了該委托引用的方法一樣。

通過按順序同步調用調用列表中的每個方法來調用其調用列表包含多個條目的委托實例。所謂的每個方法都傳遞給委托實例的同一組參數。如果這樣的委托調用包含引用參數(引用參數),則每個方法調用都將引用同一個變量; 通過調用列表中的一個方法對該變量的更改將對調用列表中的下一個方法可見。如果委托調用包括輸出參數或返回值,則它們的最終值將來自列表中最後一個委托的調用。

如果在處理調用此類委托期間發生異常,並且該異常未在調用的方法中捕獲,則在調用委托的方法中繼續搜索異常catch子句,並在調用之後繼續執行任何方法列表未被調用。

嘗試調用值為null的委托實例會導致類型異常System.NullReferenceException

以下示例顯示如何實例化,組合,刪除和調用委托:

 1 using System;
 2 
 3 delegate void D(int x);
 4 
 5 class C
 6 {
 7     public static void M1(int i) {
 8         Console.WriteLine("C.M1: " + i);
 9     }
10 
11     public static void M2(int i) {
12         Console.WriteLine("C.M2: " + i);
13     }
14 
15     public void M3(int i) {
16         Console.WriteLine("C.M3: " + i);
17     }
18 }
19 
20 class Test
21 {
22     static void Main() { 
23         D cd1 = new D(C.M1);
24         cd1(-1);                // call M1
25 
26         D cd2 = new D(C.M2);
27         cd2(-2);                // call M2
28 
29         D cd3 = cd1 + cd2;
30         cd3(10);                // call M1 then M2
31 
32         cd3 += cd1;
33         cd3(20);                // call M1, M2, then M1
34 
35         C c = new C();
36         D cd4 = new D(c.M3);
37         cd3 += cd4;
38         cd3(30);                // call M1, M2, M1, then M3
39 
40         cd3 -= cd1;             // remove last M1
41         cd3(40);                // call M1, M2, then M3
42 
43         cd3 -= cd4;
44         cd3(50);                // call M1 then M2
45 
46         cd3 -= cd2;
47         cd3(60);                // call M1
48 
49         cd3 -= cd2;             // impossible removal is benign
50         cd3(60);                // call M1
51 
52         cd3 -= cd1;             // invocation list is empty so cd3 is null
53 
54         cd3(70);                // System.NullReferenceException thrown
55 
56         cd3 -= cd1;             // impossible removal is benign
57     }
58 }

如語句所示cd3 += cd1;,委托可以多次出現在調用列表中。在這種情況下,每次出現只需調用一次。在諸如此類的調用列表中,當刪除該委托時,調用列表中的最後一個實例是實際刪除的那個。

在執行最終語句之前,cd3 -= cd1;委托cd3引用空的調用列表。嘗試從空列表中刪除委托(或從非空列表中刪除不存在的委托)不是錯誤。

產生的輸出是:

 1 C.M1: -1
 2 C.M2: -2
 3 C.M1: 10
 4 C.M2: 10
 5 C.M1: 20
 6 C.M2: 20
 7 C.M1: 20
 8 C.M1: 30
 9 C.M2: 30
10 C.M1: 30
11 C.M3: 30
12 C.M1: 40
13 C.M2: 40
14 C.M3: 40
15 C.M1: 50
16 C.M2: 50
17 C.M1: 60
18 C.M1: 60

C#6.0語言規範(十五) 委托