委托是一個類型。C#中的委托是面向對象的,並且它是類型安全的 當創建委托實例的時候,創建的實例會包含一個調用列表,在調用列表中可以包含多個方法。每個方法稱作一個調用實體。調用實體可以是靜態方法,也可以是實例方法。如果是實例方法,則該調用實體包含調用該實例方法的實例。委托並不關心它所調用方法所屬的類, ...
委托是一個類型。C#中的委托是面向對象的,並且它是類型安全的 當創建委托實例的時候,創建的實例會包含一個調用列表,在調用列表中可以包含多個方法。每個方法稱作一個調用實體。調用實體可以是靜態方法,也可以是實例方法。如果是實例方法,則該調用實體包含調用該實例方法的實例。委托並不關心它所調用方法所屬的類,它只關心被調用方法與委托的類型是否相容。 下麵是代碼實例:
1 using System; 2 namespace LycheeTest{ 3 public delegate void D(int a, int b); 4 public class Test { 5 public D myDelegate; 6 public Test() { 7 myDelegate = new D(Show1); 8 } 9 private static void Show1(int a, int b) { 10 Console.WriteLine("方法 Show1 被調用,兩個實參相加的值是:{0}", a + b); 11 } 12 private void Show2(int a, int b) { 13 Console.WriteLine("方法 Show2 被調用,兩個實參相加的值是:{0}", a + b); 14 } 15 private void Show3(int a, int b) { 16 Console.WriteLine("方法 Show3 被調用,兩個實參相加的值是:{0}", a + b); 17 } 18 } 19 public class Program { 20 static void Main(string[] args) { 21 Test myT = new Test(); 22 myT.myDelegate(33, 22); 23 Console.ReadKey(); 24 } 25 } 26 27 }
這段代碼演示的是最簡單的一種委托形式。委托類型可以定義在類的外部,也可以定義在類的內部。 本段代碼是定義在類的外部。第 3 行代碼定義的就是一個委托類型,委托類型的關鍵字是 delegate,關鍵字前是委托類型的訪問許可權修飾符。關鍵字後是委托類型的返回類型,這個返回類型規定與委托類型相容 的方法的返回類型必須與之相同。返回類型之後是委托類型的名稱。接下來是形參列表,它指定與委托類 型相容的方法的參數類型和個數必須與之相同。第 5 行代碼定義了一個委托類型的變數,它是一個實例欄位,訪問許可權是 public 的。註意委托類型欄位的訪問許可權一定要比委托類型的訪問許可權低或與委托類型的訪問許可權相同才可以。第 9 行、第 12 行和第 15 行代碼定義了三個方法。其中第 9 行代碼是一個靜態方法。因為這段代碼演示的是最簡單的委托使用方法,所以只使用了其中的靜態方法。在第 6 行的構造方法中,實例化了委托類型的變數,註意為委托變數的調用列表添加方法,只需要向其構造方法中傳遞方法名稱即可。這是為委托添加調用方法的最基本的一種方法。第 21 行定義了 Test 類的一個實例,然後第 22 行調用了類的委托成員。在調用委托成員的時候,需要向其形參列表傳遞實參。這就是最基本的委托的使用方法。這段代碼的執行結果如下:
方法 Show1 被調用,兩個實參相加的值是:55
下麵再介紹一種委托類型的使用方法,實例代碼如下:
1 using System; 2 namespace LycheeTest { 3 public delegate void D(int a, int b); 4 public class Test { 5 public static void Show1(int a, int b) { 6 Console.WriteLine("方法 Show1 被調用,兩個實參相加的值是:{0}", a + b); 7 } 8 public void Show2(int a, int b) { 9 Console.WriteLine("方法 Show2 被調用,兩個實參相加的值是:{0}", a + b); 10 } 11 public void Show3(int a, int b) { 12 Console.WriteLine("方法 Show3 被調用,兩個實參相加的值是:{0}", a + b); 13 } 14 } 15 public class Program { 16 static void Main(string[] args) { 17 Test myT = new Test(); 18 D myDelegate = new D(Test.Show1); 19 D myDelegate1 = new D(myT.Show2); 20 D myDelegate2 = new D(myT.Show3); 21 myDelegate(22, 33); 22 myDelegate1(33, 44); 23 myDelegate2(55, 66); 24 Console.ReadKey(); 25 } 26 } 27 28 }
這段代碼取消了類中的委托類型欄位,而是將委托類型作為一個類來看待。在包含入口點方法的類中,首先第 17 行定義了 Test 類的一個變數並做了實例化。因為要向委托傳遞類的實例方法,所以必須有類的實 例存在,才能引用類的實例方法。第 18 行定義了一個委托類型的變數,並實例化,這裡需要註意,因為委托並不是類中的一個成員了, 所以向其構造方法傳遞靜態方法的時候,需要以類名引用。第 19 行也定義了一個委托類型的變數,在向其傳遞實例方法的時候,需要以類的實例來引用。第 20 行代碼的情況同第 19 行代碼一樣。在向委托傳遞方法的時候,需要傳遞方法名,而不需要方法的形參列表。第 21 行到第 23 行是對委托的調用,這時要為其傳遞方法的實參。這段代碼的執行結果如下:
方法 Show1 被調用,兩個實參相加的值是:55 方法 Show2 被調用,兩個實參相加的值是:77 方法 Show3 被調用,兩個實參相加的值是:121
委托的訪問修飾符
當委托位於類的外部時,可以使用的訪問修飾符包括 public 和 internal。如果什麼也不寫,預設是internal 的。當委托位於類的內部時,可以使用的訪問修飾符包括 public、protected、internal、protected
1 using System; 2 namespace LycheeTest{ 3 public class Test { 4 protected delegate void D(int a, int b); 5 private delegate void D1(int a, int b); 6 protected internal delegate void D2(int a, int b); 7 internal delegate void D3(int a, int b); 8 private D myD; 9 private D1 myD1; 10 private D2 myD2; 11 private D3 myD3; 12 public Test() { 13 myD = new D(Show1); 14 myD1 = new D1(Show1); 15 myD2 = new D2(Show1); 16 myD3 = new D3(Show1); 17 } 18 public static void Show1(int a, int b) { 19 Console.WriteLine("方法 Show1 被調用,兩個實參相加的值是:{0}", a + b); 20 } 21 public void Show2(int a, int b) { 22 Console.WriteLine("方法 Show2 被調用,兩個實參相加的值是:{0}", a + b); 23 } 24 public void Show3(int a, int b) { 25 Console.WriteLine("方法 Show3 被調用,兩個實參相加的值是:{0}", a + b); 26 } 27 public void Use() { 28 myD(11, 12); 29 myD1(22, 45); 30 myD2(55, 78); 31 myD3(345, 100); 32 } 33 } 34 class Test1: Test { 35 private D Test1D; 36 private D2 Test1D2; 37 private D3 Test1D3; 38 public Test1() { 39 Test1D = new D(Test.Show1); 40 Test1D2 = new D2(Test.Show1); 41 Test1D3 = new D3(Test.Show1); 42 } 43 public void Use1() { 44 Test1D(22, 45); 45 Test1D2(44, 45); 46 Test1D3(77, 78); 47 } 48 } 49 public class Program { 50 static void Main(string[] args) { 51 Test1 myT1 = new Test1(); 52 myT1.Use(); 53 myT1.Use1(); 54 Console.ReadKey(); 55 } 56 } 57 }
代碼的第 4 行在類的內部定義了委托類型,它作為類的成員定義,訪問許可權是 protected,它可以被本類內部訪問,也可以被派生類訪問。代碼的第 5 行定義的委托類型,訪問許可權是 private 的,它只可以被本類內部訪問。代碼的第 6 行定義的 protected internal 訪問許可權的委托類型,可以被本程式集訪問, 還可以被派生類訪問,而不管派生類位於哪個程式集。第 7 行定義的委托類型是 internal 的,它只可以被本程式集訪問。因為所有這幾種委托類型都可以被本類內部訪問,所以第 10 行到第 13 行定義了它們的變數。第 12 行的實例構造方法中,對這四個委托類型的變數進行了實例化,併為它們的調用列表加入了方法 Show1。Show1 是一個靜態方法,但是在類內部傳入委托類型的構造方法時,不需要使用類名引用。第 27 行定義了實例方法,在方法內部調用了這四個委托,併為其傳入實參。第 34 行代碼又定義了一個類,它繼承自基類 Test。因為基類中的委托類型只有 D、D2 和 D3 可以被派生類訪問,所以第 35 行到第 37 行定義了它們的變數。註意,雖然它們和基類中的委托變數是同一種類型, 但是它們是不同的委托。在第 38 行的實例構造方法中,為這三個委托類型的變數創建實例,併為其調用列表加入方法,因為靜態方法 Show1 也被派生類所繼承,所以這裡傳入的方法名,可以使用類名引用,也可以不使用類名引用。 第 43 行定義了一個實例方法,方法內部調用了這三個委托,併為其傳入實參。第 51 行定義了派生類的實例,然後調用實例方法Use和Use1。這段代碼的執行結果如下:
方法 Show1 被調用,兩個實參相加的值是:23 方法 Show1 被調用,兩個實參相加的值是:67 方法 Show1 被調用,兩個實參相加的值是:133 方法 Show1 被調用,兩個實參相加的值是:445 方法 Show1 被調用,兩個實參相加的值是:67 方法 Show1 被調用,兩個實參相加的值是:89 方法 Show1 被調用,兩個實參相加的值是:155
因為 D 和 D2 的訪問許可權被定義成了 protected 和 protected internal。所以下麵來驗證在其它程式集中是否可以訪問它們。首先要將本段代碼中的包含 Main 方法的類去掉,然後在它的項目屬性中將它改變為類庫。接下來新建一個控制台項目,並物理上引用這個類庫。控制台項目的代碼如下:
1 using System; 2 using LycheeTest; 3 namespace LycheeTest1{ 4 class Program: Test { 5 private D pD; 6 private D2 pD2; 7 public Program() { 8 pD = new D(Show1); 9 pD2 = new D2(Show1); 10 } 11 public void Use3() { 12 pD(34, 33); 13 pD2(12, 11); 14 } 15 static void Main(string[] args) { 16 Program p = new Program(); 17 p.Use3(); 18 Console.ReadKey(); 19 } 20 } 21 }
因為第 3 行代碼的命名空間和類庫的命名空間是兩個獨立的命名空間,它們的成員不位於同一個命名空間內。所以在一個命名空間內引用另一個命名空間的成員時,需要加上另一個命名空間的名稱進行引用。 為了代碼編寫的方便,第 2 行代碼首先引用了類庫的命名空間。第 4 行代碼定義了一個類,它繼承自基類 Test。因為是派生類,所以對於委托類型 D 和 D2 都可以訪 問。第 5 行代碼和第 6 行代碼分別定義了 D 和 D2 的兩個變數。第 7 行的實例構造方法對這兩個變數進行了實例化,併為其傳入方法 Show1。因為 Show1 方法被繼承了下來,所以這裡不需要類名引用。第 11 行代碼定義了一個實例方法,它的作用是調用這兩個委托,併為其傳入實參。第 16 行代碼定義了本類的一個實例,並調用了實例方法 Use3。這段代碼的執行結果如下:
方法 Show1 被調用,兩個實參相加的值是:67 方法 Show1 被調用,兩個實參相加的值是:23類Test中的委托類型D2和D3都具有internal許可權,現在來驗證一下,對於一個同一程式集中的非派生類是否可以訪問它們。首先將類庫更改回控制台項目,然後增加一個類,這個類對於Test類來說是獨立的。它們之間只是位於一個程式集內,彼此沒有繼承關係。代碼如下:
1 using System; 2 namespace LycheeTest { 3 public class Test { 4 protected delegate void D(int a, int b); 5 private delegate void D1(int a, int b); 6 protected internal delegate void D2(int a, int b); 7 internal delegate void D3(int a, int b); 8 private D myD; 9 private D1 myD1; 10 private D2 myD2; 11 private D3 myD3; 12 public Test() { 13 myD = new D(Show1); 14 myD1 = new D1(Show1); 15 myD2 = new D2(Show1); 16 myD3 = new D3(Show1); 17 } 18 public static void Show1(int a, int b) { 19 Console.WriteLine("方法 Show1 被調用,兩個實參相加的值是:{0}", a + b); 20 } 21 public void Show2(int a, int b) { 22 Console.WriteLine("方法 Show2 被調用,兩個實參相加的值是:{0}", a + b); 23 } 24 public void Show3(int a, int b) { 25 Console.WriteLine("方法 Show3 被調用,兩個實參相加的值是:{0}", a + b); 26 } 27 public void Use() { 28 myD(11, 12); 29 myD1(22, 45); 30 myD2(55, 78); 31 myD3(345, 100); 32 } 33 } 34 35 class Test1 { 36 private Test.D2 tD2; 37 private Test.D3 tD3; 38 public Test1() { 39 tD2 = new Test.D2(Test.Show1); 40 tD3 = new Test.D3(Test.Show1); 41 } 42 public void Use3() { 43 tD2(34, 33); 44 tD3(22, 21); 45 } 46 } 47 public class Program { 48 static void Main(string[] args) { 49 Test1 myT1 = new Test1(); 50 myT1.Use3(); 51 Console.ReadKey(); 52 } 53 } 54 }
這段代碼中,原來的類Test沒有進行修改。在第35行上,定義了一個類,它是一個相對於Test類來說獨立的類。它們的關係僅限於同在一個程式集內。第 36 行代碼和第 37 行代碼定義了委托類型D2和D3的兩個變數。這裡需要註意,因為這兩個類不是繼承關係,所以要引用Test類中的這兩個委托類型需要使用Test類的類名進行引用。第 38 行代碼是實例構造方法,在構造方法中將委托實例化。實例化委托類型的時候,仍然需要使用類名引用委托類型名,傳遞的方法名也是如此。第 行42 定義了一個實例方法,它調用了委托,併為其傳入了實參。第 49 行代碼定義了類Test1的一個實例,然後第 61 行調用類的實例方法。這段代碼的執行結果如下:
方法 Show1 被調用,兩個實參相加的值是:67 方法 Show1 被調用,兩個實參相加的值是:43