建議39:了解委托的實質
理解C#中的委托需要把握兩個要點:
1)委托是方法指針。
2)委托是一個類,當對其進行實例化的時候,要將引用方法作為它的構造方法的參數。
設想這樣一個場景:在點對點文件傳輸過程當中,我們要設計一個文件傳輸類,該傳輸類起碼要滿足下面幾項功能:
由於要讓通知本身能夠被控制台程序和WinFrom應用程序使用,因此設計這個文件傳輸類在進行進度通知時,就不能顯示調用:
Console.WriteLine("當前進度:"+fileProgress);
或者
this.progressText.Text = "當前進度:" + fileProgress;
理想情況下是,在需要通知的地方,全部將其置換成一個方法的指針,由調用者來決定該方法完成什麼功能。這個方法指針在C#中就是委托。可以像下面那樣聲明委托:
public delegate void FileUploadedHandler(int progress);
這個文件傳輸類可以寫成這樣:
class FileUploader { public delegate void FileUploadedHandler(int progress); public FileUploadedHandler FileUploaded; public void Upload() { int fileProgress = 100; while (fileProgress > 0) { //傳輸代碼,省略 fileProgress--; if (FileUploaded != null) { FileUploaded(fileProgress); } } } }
調用者在調用這個文件傳輸類的時候,應該同時為FileUploaded賦值,賦值過程中也就是將自身所具有的和委托聲明相同的聲明方法賦值給FileUploaded。這樣,類型FileUploader在執行到下面的代碼時,就是執行調用者自身的方法
FileUploaded(fileProgress);
理解了“委托是方法指針”這一點後,在了理解“委托是一個類”。
查看下面這句話:
public delegate void FileUploadedHandler(int progress);
它的IL代碼為:
.class auto ansi sealed nested public FileUploadedHandler extends [mscorlib]System.MulticastDelegate { .method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed { } .method public hidebysig newslot virtual instance class [mscorlib]System.IAsyncResult BeginInvoke(int32 progress, class [mscorlib]System.AsyncCallback callback, object 'object') runtime managed { } .method public hidebysig newslot virtual instance void EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed { } .method public hidebysig newslot virtual instance void Invoke(int32 progress) runtime managed { } }
調用委托方法:
FileUploaded(fileProgress);
其實是調用:
FileUploaded.Invok(fileProgress);
可以查看Upload方法的IL代碼:
.method public hidebysig instance void Upload() cil managed { .maxstack 2 .locals init ( [0] int32 fileProgress, [1] bool CS$4$0000) L_0000: nop L_0001: ldc.i4.s 100 L_0003: stloc.0 L_0004: br.s L_0028 L_0006: nop L_0007: ldloc.0 L_0008: ldc.i4.1 L_0009: sub L_000a: stloc.0 L_000b: ldarg.0 L_000c: ldfld class MyTest.FileUploader/FileUploadedHandler MyTest.FileUploader::FileUploaded L_0011: ldnull L_0012: ceq L_0014: stloc.1 L_0015: ldloc.1 L_0016: brtrue.s L_0027 L_0018: nop L_0019: ldarg.0 L_001a: ldfld class MyTest.FileUploader/FileUploadedHandler MyTest.FileUploader::FileUploaded L_001f: ldloc.0 L_0020: callvirt instance void MyTest.FileUploader/FileUploadedHandler::Invoke(int32) L_0025: nop L_0026: nop L_0027: nop L_0028: ldloc.0 L_0029: ldc.i4.0 L_002a: cgt L_002c: stloc.1 L_002d: ldloc.1 L_002e: brtrue.s L_0006 L_0030: ret }
可以看到L_0020處調用Invoke方法。
一句話:委托是一種數據類型,它用來傳遞方法。
轉自:《編寫高質量代碼改善C#程序的157個建議》陸敏技