c#委託(delegate)揭秘

來源:互聯網
上載者:User

委託是一種定義方法簽名的類型。 當執行個體化委託時,您可以將其執行個體與任何具有相容簽名的方法相關聯。 您可以通過委託執行個體調用方法。  

在表面上,委託很簡單,使用new來構造委託執行個體。使用委託執行個體的變數名來調用回呼函數。實際情況是編譯器,CLR在幕後做了大量的工作來隱藏其複雜性,只有瞭解了這些幕後的東西,你才能真正的掌握它、靈活的運用它。

     1、聲明委託

namespace DelegateDemo{    internal delegate void HelloCallBack(string name);    class Program    {        static void Main(string[] args)        {        }    }}

 

    通過ildasm查看中間代碼,如下

編譯器自動產生一個helloCallBack的類,類裡面有構造方法,回調方法Invoke,非同步回調方法(BeginInvoke,EndInvoke),

它繼承MulticastDelegate類,MulticastDelegate繼承Delegate類,c#有兩個委託類(Delegate,MulticastDelegate)是有曆史原因的,原來是要合并成一個類,但快到發布時間了,合并它需要重新測試,所以Delegate就倖存下來)。

  2、委託的執行個體化

    internal delegate void HelloCallBack(string name);    class Program    {        static void Main(string[] args)        {            HelloCallBack helloShow = new HelloCallBack(ShowName);            Console.ReadLine();        }        static void ShowName(string name)        {            Console.WriteLine(name);        }            }

 

      中間代碼如下

     .method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint  /                                                                                                 //第一個被執行的方法被稱為入口函數
  // Code size       15 (0xf)
  .maxstack  3                                                                                                  //定義函數代碼所用堆棧的最大深度
  .locals init ([0] class DelegateDemo.HelloCallBack helloShow)                                //分配一個局部變數helloShow

  IL_0000:  nop                                                                                                 //如果修補作業碼,則填充空間,未執行任何有意義的操作

  IL_0001:  ldnull                                                                                                //將Null 參考推送到計算堆棧上
  IL_0002:  ldftn      void DelegateDemo.Program::ShowName(string)                      //將ShowName的函數指標(非託管指標 native int 類型)推送到計算堆棧上
  IL_0008:  newobj     instance void DelegateDemo.HelloCallBack::.ctor(object, native int) //建立一個新對象,並將對象引用推送到計算堆棧上
  IL_000d:  stloc.0                                                                                               //從計算堆棧的頂部彈出當前值並將其儲存到索引 0 處的局部變數helloShow中
  IL_000e:  ret                                                                                                    //從當前方法返回,並將傳回值(如果存在)從調用方的計算堆棧推送到被呼叫者的計算堆棧上
} // end of method Program::Main

  從中間代碼我們可以看到,HelloCallBack類建構函式有兩個參數,HelloCallBack::.ctor(object,  native int),而My Code是

HelloCallBack helloShow = new HelloCallBack(ShowName);只有一個參數,應該編譯不過。編譯器在這個地方幫我們做了一些東西,

當它知道要構造的是委託時,就會分析原始碼來確定引用的是哪個對象,那個方法。對象引用傳遞給object,ShowName的函數指標傳遞給native int

    3、調用回調方法

     原始碼如下

    internal delegate void HelloCallBack(string name);    class Program    {        static void Main(string[] args)        {            HelloCallBack helloShow = new HelloCallBack(ShowName);            helloShow("hello");        }        static void ShowName(string name)        {            Console.WriteLine(name);            Console.ReadLine();        }             }

 中間代碼如下

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       27 (0x1b)
  .maxstack  3
  .locals init ([0] class DelegateDemo.HelloCallBack helloShow)
  IL_0000:  nop
  IL_0001:  ldnull
  IL_0002:  ldftn      void DelegateDemo.Program::ShowName(string)
  IL_0008:  newobj     instance void DelegateDemo.HelloCallBack::.ctor(object,
                                                                       native int)
  IL_000d:  stloc.0
  IL_000e:  ldloc.0                                    //將索引 0 處的局部變數載入到計算堆棧上
  IL_000f:  ldstr      "hello"                        //把一個字串常量裝入堆棧
  IL_0014:  callvirt   instance void DelegateDemo.HelloCallBack::Invoke(string) //對對象調用後期Binder 方法,並且將傳回值推送到計算堆棧上

  IL_0019:  nop
  IL_001a:  ret
} // end of method Program::Mains

 

helloShow("hello") 等價於 helloShow.Invoke("hello");

完整的代碼如下

    internal delegate void HelloCallBack(string name);    class Program    {        static void Main(string[] args)        {            HelloCallBack helloShow = new HelloCallBack(ShowName);            helloShow.Invoke("hello");
 Console.ReadLine();
 }        static void ShowName(string name)        {            Console.WriteLine(name);        }             }

 Invoke是怎麼實現的呢,查看中間代碼如下

.method public hidebysig newslot virtual         instance void  Invoke(string name) runtime managed{} // end of method HelloCallBack::Invoke

 runtime managed 表示此方法運行時有CLR處理,我推測類似於

            Delegate[] delegates = helloShow.GetInvocationList();
            for (int i = 0; i < delegates.Length; i++)
            {
                Delegate callback = delegates[i];
                MethodInfo method = callback.Method;
                method.Invoke(helloShow.Target, new object[] { "hello" });
            }

 4、委託鏈

委託鏈是委派物件的集合,利用它,可以調用委託的所有方法

Delegate有兩個公用屬性

Target  擷取類執行個體,當前委託將對其調用執行個體方法。(靜態方法訪問空)

Method 擷取委託所表示的方法。

多播委託的使用如下

namespace DelegateDemo{    internal delegate void HelloCallBack(string name);    class Program    {        static void Main(string[] args)        {            HelloCallBack helloShow = new HelloCallBack(ShowName);            helloShow += ShowCHName;            helloShow.Invoke("hello");            Console.ReadLine();        }        public static void ShowCHName(string name)        {            Console.WriteLine("你好:"+name);        }            public static void ShowName(string name)        {            Console.WriteLine(name);        }             }}

 查看MulticastDelegate源碼可知,委託鏈儲存在private object _invocationList;

HelloCallBack helloShow = new HelloCallBack(ShowName);

_invocationList被初始化成object[],數組的第一個元素為new HelloCallBack(ShowName)委託

 helloShow += ShowCHName;

 +使用了運算子多載,它實際調用的是Delegate的Combine,-號實際調用的是Delegate的Remove




 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.