什麼是委托?還記得C/C++語言里的函數指針嗎?委托就是他的”升級版“。先看一個簡單的小程式: 直接調用和間接調用他們的運行結果是一樣的變數和函數到底是啥?變數(代表數據)是以變數名對應的記憶體地址為起點的一段記憶體中所存儲的值函數(代表演算法)是以函數名對應的記憶體地址為起點的一段記憶體中所存儲的一組機器語 ...
什麼是委托?
還記得C/C++語言里的函數指針嗎?委托就是他的”升級版“。
先看一個簡單的小程式:
1 # include<stdio.h>
2
3 typedef int(*Calc)(int a, int b);//使用typedef把他定義成一種數據類型
4
5 int Add(int a,int b)
6 {
7 int result = a + b;
8 return result;
9 }
10
11 int main(void)
12 {
13 int x = 100;
14 int y = 200;
15 int z = 0;
16
17 z = Add(x,y);//直接調用
18 printf("%d+%d=%d\n",x,y,z);
19
20 Calc cFun = &Add;//間接調用
21 z = cFun(x, y);
22 printf("%d+%d=%d\n", x, y, z);
23
24 return 0;
25 }
直接調用和間接調用他們的運行結果是一樣的
變數和函數到底是啥?
變數(代表數據)是以變數名對應的記憶體地址為起點的一段記憶體中所存儲的值
函數(代表演算法)是以函數名對應的記憶體地址為起點的一段記憶體中所存儲的一組機器語言指令
知道了啥是變數和函數,那就來看看啥是 直接調用和間接調用
直接調用:通過函數名來調用函數,CPU通過函數名直接獲得函數所在地址並開始執行,執行完函數後CPU返回調用者那裡繼續往下執行
間接調用:通過函數指針來調用函數,CPU通過讀取函數指針存儲的值(函數名所對應的地址)獲得函數所在地址並開始執行,最終返回調用者那裡繼續往下執行
下麵我們就看看委托是個什麼東西
1 namespace DelegateExample_Action_
2 {
3 class Program
4 {
5 static void Main(string[] args)
6 {
7 Calculator calculator = new Calculator();
8 Action action = new Action(calculator.Report);
9 action.Invoke();//間接引用
10 /*
11 還可以這麼寫
12 action();
13 */
14 calculator.Report();//直接引用
15 //運行結果完全一樣
16 }
17 }
18
19 class Calculator
20 {
21 public void Report()
22 {
23 Console.WriteLine("I have three methods.");
24 }
25 public int Add(int a, int b)
26 {
27 int result = a + b;
28 return result;
29 }
30 public int Sub(int a, int b)
31 {
32 int result = a - b;
33 return result;
34 }
35 }
36 }
IDE提示到,你要往裡面放一個返回值為void類型,參數列表為空的一個方法,那麼在Calculator類里符合的就是Report方法,所以我們就把Report方法放了進去
運行結果:
第一行是我們直接調用時列印出來的語句,第二行是我們用Action委托列印出來的語句
如果要調用Add或者Sub方法,我們選擇使用Func委托:
1 namespace DelegateExample_Func_ 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 Calculator calculator = new Calculator(); 8 9 Func<int, int, int> Func1 = new Func<int, int, int>(calculator.Add); 10 Func<int, int, int> Func2 = new Func<int, int, int>(calculator.Sub); 11 int x = 100; 12 int y = 30; 13 int z = 0; 14 z = Func1.Invoke(x, y); 15 Console.WriteLine(z); 16 z = Func2.Invoke(x, y); 17 Console.WriteLine(z); 18 } 19 } 20 21 class Calculator 22 { 23 public int Add(int a, int b) 24 { 25 int result = a + b; 26 return result; 27 } 28 public int Sub(int a, int b) 29 { 30 int result = a - b; 31 return result; 32 } 33 } 34 }
在打尖括弧時,IDE提示我們他有17個重載,通過下翻會發現,它會增加參數的個數(最後的那個是返回值類型),我們要用他來引用Add和Sub方法,而這兩個方法有兩個參數,所以我們就選了圖片里的那種情況,因為兩個參數是int類型,所以返回的結果也一定是int類型 所以就用 Func<int,int,int>(Calculator.Add); (尖括弧里前兩個是參數類型,最後一個是返回值類型)
運行結果:
自定義委托
以上的Action委托和Function委托,是微軟已經為我們準備好的,那我們自己怎麼聲明呢,這就是接下來的自定義委托
先說明,委托是一種類(class),所以委托聲明在名稱空間內,和program類是平級的。如何查看委托是不是類呢,很簡單的一個小程式:
1 namespace DelegateExample_IsClass_ 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 Type t = typeof(Action); 8 Console.WriteLine(t.IsClass); 9 } 10 } 11 }
結果運行後列印出來的是 True ,這說明委托確實是個類
然後我們就自己聲明一個委托,委托的聲明格式是這樣的:
可訪問性 delegate關鍵字 目標方法返回值類型 委托名字(目標方法的參數列表);
下麵看一個小程式:
1 namespace DelegateExample_custom_delegate_ 2 { 3 public delegate double Calc(double x,double y); 4 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 Calculator calculator = new Calculator(); 10 Calc calc = new Calc(calculator.Add); 11 Calc calc2 = new Calc(calculator.Sub); 12 Calc calc3 = new Calc(calculator.Mul); 13 Calc calc4 = new Calc(calculator.Div); 14 15 double a = 100; 16 double b = 200; 17 double c = 0; 18 19 c = calc.Invoke(a, b); 20 Console.WriteLine(c); 21 c = calc2.Invoke(a, b); 22 Console.WriteLine(c); 23 c = calc3.Invoke(a, b); 24 Console.WriteLine(c); 25 c = calc4.Invoke(a, b); 26 Console.WriteLine(c); 27 28 } 29 } 30 class Calculator 31 { 32 public double Add(double x, double y) 33 { 34 return x + y; 35 } 36 public double Sub(double x, double y) 37 { 38 return x - y; 39 } 40 public double Mul(double x, double y) 41 { 42 return x * y; 43 } 44 public double Div(double x, double y) 45 { 46 return x / y; 47 } 48 } 49 }
這個程式中,我們自己聲明瞭一個叫Calc的委托,因為我們的方法都是double的參數,double的返回值,所以是 "double Calc" 。剩下的就沒什麼大問題了。
自定義委托時有幾點需要註意:
委托與所封裝的方法必須“類型相容”,也就是說
返回值的數據類型要一致
參數列表在個數和數據類型上一致(參數名不需要一樣)
再就是聲明的時候要聲明在正確的位置處,要聲明在名稱空間中(與program平級),但是C#又允許嵌套類型存在,聲明在Program類里又編譯的過去,但不要這麼乾,該放哪就放哪(要放在名稱空間里與其他類平級)
----------------------------------------------------------------------------------------------
迫於時間緣故,這篇博客就先寫這麼多,放假後會寫完後面的
希望博友們指出我的問題所在,指出我哪裡理解錯了,共同交流,共同進步!
To be continued!