什么是委托
要傳遞方法,就必須把方法的細節封裝在一鐘新類型的對象中,即委托。委托是一種特殊類型的對象,其特殊之處在于,我們以前定義的所有對象都包含數據,而委托只包含一個或多個方法的地址。
.NET版本中,委托指向方法的地址。在C++中,函數指針是一個指向內存位置的指針,但它不是類型安全的。開發者無法判斷這個指針實際指向什么,像參數和返回值等項就更不知道了。
.NET委托是類型安全的類,它定義了返回類型和參數的類型。委托類不僅包含對方法的引用,也可以包含對多個方法的引用。
可以認為委托是持有一個或多個方法的對象。委托可以被執行,執行委托時委托會執行它所“持有”的方法
代碼示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForDelegate { //使用關鍵字delegate聲明委托類型 //委托是一種類型,所以它與類屬于同一級別 //注意:這里是委托類型,而不是委托對象 delegate void MyDel( int value); class Program { void PrintLow( int value) { Console.WriteLine( "{0} - Low Value" , value); } void PrintHigh( int value) { Console.WriteLine( "{0} - High Value" , value); } static void Main( string [] args) { //實例化Program類,以訪問PrintLow和PrintHigh方法 Program program = new Program(); //聲明一個MyDel類型的委托 MyDel del; //創建隨機數 Random rand = new Random(); int randomvalue = rand.Next(99); //使用三目運算符根據當前隨機數的值來創建委托對象 del = randomvalue < 50 ? new MyDel(program.PrintLow) : new MyDel(program.PrintHigh); //執行委托 del(randomvalue); Console.ReadKey(); } } } |
從上例可以看出,使用委托的首先得通過關鍵字delegate聲明一個委托類型,這個委托類型包括返回值、名稱、簽名;當類型聲明好以后,需要通過new來創建委托對象,創建對象時的參數是一個方法,這個方法的簽名和返回類型必須與該委托類型定義的簽名一致;調用委托時,直接通過實例化的委托對象名,并提供參數即可,然后委托會執行在其所持有的方法
委托與類
委托和類一樣,是一種用戶自定義的類型;不同的是類表示的是數據和方法的集合,而委托持有一個或多個方法,以及一系列預定義操作
委托的使用步驟
- 聲明一個委托類型
- 使用該委托類型聲明一個委托變量
- 創建委托類型的對象,把它賦值給委托變量;委托對象中包括指向某個方法的引用,此方法和委托類型定義的簽名與返回類型需要一致
- 增加更多的方法(可選)
- 像調用方法一樣調用委托(委托中的包含的每一個方法都會被執行)
delegate的原則
delegate相當于一個包含有序方法列表的對象,這些方法都具有相同的簽名和返回類型
方法的列表稱為調用列表
委托保存的方法可以來自任何類或結構,只要它們在以下兩點匹配:
- 委托的返回類型
- 委托的簽名(包括ref和out修飾符)
調用列表中的方法可以是靜態方法也可以是實例方法
在調用委托的時候,會調用列表中的所有方法
聲明委托類型
如下,delegate關鍵字開關,然后是返回類型,再定義名稱與簽名
1
|
delegate void MyDel( int vallue); |
返回類型與簽名指定了委托接受的方法形式
注意:委托類型是沒有方法主體的
創建委托對象
使用new運算符創建對象
1
2
|
MyDel del = new MyDel( object .Func); //object.Func是個實例方法 Mydel _del = new MyDel(Object.Func); //Object.Func是個靜態方法 |
使用快捷語法創建對象
1
2
|
MyDel del = object .Func; //object.Func是個實例方法 Mydel _del = Object.Func; //Object.Func是個靜態方法 |
這種語法是能夠工作是因為在方法名稱和其相應的委托類型之間存在隱式的轉換
創建委托對象后會將指定的方法加入到委托的調用列表中
由于委托是引用類型,可以通過賦值來改變包含在委托變量中的引用,如下:
1
2
3
|
MyDel del; del = new MyDel( object .FuncA); //創建第一個對象 del = new MyDel( object .FuncB); //創建第二個對象 |
由于第二個對象也賦值給了變量del,因此del所引用的第一個對象將被垃圾回收器回收
組合委托
1
2
3
4
5
6
|
//創建兩個委托 MyDel del_A = new MyDel( object .FuncA); Mydel del_B = new MyDel( object .FuncA); //組合委托 MyDel del_C = del_A + del_B; |
當將del_A與del_B通過+進行組合后,會返回一個新的委托對象,該對象將del_A與del_B中的方法調用列表組合到新的對象里,該新對象賦值給變量del_C,所以執行del_C的時候,會執行del_A與del_B中所保存的方法object.FuncA和object.FuncA
委托添加多個方法
1
2
3
|
MyDel del = object .FuncA; //創建并初始化委托對象 del += object .FuncB; //增加方法 del += object .FuncC; //增加方法 |
通過+=符號為委托對象添加更多方法,上例中,del對象不保存了三個方法,在執行del時,這三個方法會被依次調用
注意,在使用+=為委托對象添加新的方法時,實際上是創建了一個新的委托對象(原對象的副本)
移除委托方法
1
2
|
del -= object .FuncB; //移除方法 del -= object .FuncC; //移除方法 |
通過-=來將委托調用列表中已保存的方法,移除動作是從調用列表的最后一個方法開始匹配,一次只會移除一條匹配的方法,如果調用列表中不存在該方法,則沒有任何效果;如果試圖調用一個空的委托則會發生異常
注意,在使用-=為委托對象移除方法時,實際上是創建一個新的委托對象(原對象的副本)
調用委托
調用委托就像調用方法一樣
示例:MyDel類型參考上面的定義
1
2
3
4
5
6
|
MyDel del = object .FuncA; //創建并初始化委托對象 del += object .FuncB; //增加方法 del += object .FuncC; //增加方法 //調用委托 del(55); |
參數55會在調用委托對象時依次傳遞給保存的方法
一個完整的委托示例代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForDelegate { class Program { //定義委托類型 delegate void PrintFunction( string txt); //測試類中定義三個方法 class Test { public void PrintA( string txt) { Console.WriteLine( "printA:{0}" , txt); } public void PrintB( string txt) { Console.WriteLine( "printB:{0}" , txt); } public static void PrintC( string txt) { Console.WriteLine( "printC:{0}" , txt); } } static void Main( string [] args) { Test test = new Test(); PrintFunction pf; //實例化并創建委托對象 pf = test.PrintA; //為委托對象增加方法 pf += test.PrintB; pf += Test.PrintC; pf += test.PrintA; //添加一個重復的方法 //通過與null比較,確認委托對象中保存了方法 if (pf != null ) pf( "Hello" ); else Console.WriteLine( "pf是個空委托!" ); Console.ReadKey(); } } } |
調用帶有返回值的委托
如何委托有返回值,并且調用列表中有一個以上的方法,那么將使用最后一個方法的返回值,之前方法的返回值被忽略
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForDelegate { class Program { //定義委托類型 delegate int DelFunction(); //測試類中定義三個方法 class Test { int IntValue = 0; public int FuncA() { return IntValue += 1; } public int FuncB() { return IntValue += 10; } } static void Main( string [] args) { Test test = new Test(); DelFunction df; df = test.FuncA; df += test.FuncB; //最終返回值的是11 if (df != null ) Console.WriteLine( "返回值:" +df()); else Console.WriteLine( "pf是個空委托!" ); Console.ReadKey(); } } } |
具有引用參數的委托
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForDelegate { //定義委托類型 delegate void MyDel( ref int x); class Program { static void Add1( ref int x) { x += 1; } static void Add2( ref int x) { x += 2; } static void Main( string [] args) { Program program = new Program(); MyDel del = Add1; del += Add2; //ref會將x當作引用值傳遞給委托方法 int x = 5; del( ref x); Console.ReadKey(); } } } |
在調用Add1方法時,x = 5+1,再調用Add2方法時,不是x = 5+2而是x = 6 +2
參考:ref按引用傳遞參數
在方法的參數列表中使用 ref 關鍵字時,它指示參數按引用傳遞,而非按值傳遞。 按引用傳遞的效果是,對所調用方法中參數進行的任何更改都反映在調用方法中。 例如,如果調用方傳遞本地變量表達式或數組元素訪問表達式,所調用方法會替換 ref 參數引用的對象,然后,當該方法返回時,調用方的本地變量或數組元素將開始引用新對象
匿名方法
匿名方法是在初始化委托時內聯聲明的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForDelegate { //定義委托類型 delegate void MyDel( ref int x); class Program { static void Add1( ref int x) { x += 1; } static void Add2( ref int x) { x += 2; } static void Main( string [] args) { Program program = new Program(); //采用匿名方法形式代替具名方法 MyDel del = delegate ( ref int y) { y += 3; }; del += Add1; del += Add2; //ref會將x當作引用值傳遞給委托方法 int x = 5; del( ref x); Console.ReadKey(); } } } |
在聲明委托變量時作為初始化表達式,或在為委托增加事件時使用
語法解析
以關鍵字delegate開頭;后跟小括號提供參數;再后跟{}作為語句塊
delegate (Parameters) {ImplementationCode}
- 匿名方法不會顯示的聲明返回類型delegate (int x) { return x;}即為返回一個int類型的值
- 參數的數量、位置、類型、修飾符必須與委托相匹配
- 可以通過省略圓括號或使圓括號為空來簡化匿名方法的參數列表,前提是參數是不包含out參數,方法體中不使用任何參數
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForDelegate { //定義委托類型 delegate void MyDel( ref int x); class Program { static void Add1( ref int x) { x += 1; } static void Add2( ref int x) { x += 2; } static void Main( string [] args) { Program program = new Program(); //采用匿名方法形式代替具名方法 MyDel del = delegate ( ref int y) { y += 3; }; del += Add1; del += Add2; //匿名方法未使用任何參數,簡化形式 del += delegate { int z = 10;}; //ref會將x當作引用值傳遞給委托方法 int x = 5; del( ref x); Console.ReadKey(); } } } |
如果定義一個帶有params形式的參數,在使用匿名方法的時候可以省略params關鍵字以簡化代碼
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeForDelegate { //定義一個帶有params形式參數的委托類型 delegate void DelFunction( int x, params int [] z); class Program { static void Main( string [] args) { Program program = new Program(); // 關鍵字params被忽略(省略關鍵字以簡化) DelFunction df = delegate ( int x, int [] y) { ... }; Console.ReadKey(); } } } |
Lambda表達式
Lambda可以簡化匿名方法,語法形式如下:
(參數) => {語句塊} // => 讀作 gose to
參數中的類型可以省略
如果只有一個參數,圓括號可以省略
如果沒有參數,圓括號不可以省略
語句塊如果只有一行代碼,花括號可以省略
示例:
1
2
3
4
|
MyDel del = delegate ( int y) { return y += 3; }; //匿名方法 MyDel del1 = ( int y) => { return y += 3;} // Lambda表達式 MyDel del2 = (y) => { return y += 3;} // 省略參數類型 MyDel del3 = y => y += 3; // 省略圓括號和花括號,雖然沒有return,但仍會返回y的值 |
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。
原文鏈接:https://www.jianshu.com/p/6935eaad5ff8