詳解C#委托和事件(二)


  一、當我們使用關鍵字delegate聲明一個自定義委托類型時,實際上是聲明了一個該名稱的類類型,繼承自抽象類System.MulticastDelegate,還包含實例方法Invoke、BeginInvoke、EndInvoke:

  public delegate void MyDelegate();

  

  其中的構造函數中第二個參數是native int類型的,這個是什么呢?我們接着看:

  我們知道在C#中任何方法都可以直接賦值給簽名一致的委托實例,這個過程看似並不合理,按理來說C#中不支持直接獲取函數的指針,其實這里是由編譯器進行了取址操作,查看IL代碼可知:

  MyDelegate myDelegate = myObj.MyFunc;

  

  可以看到由編譯器為我們進行了構建委托實例的過程,而且這里調用了ldftn命令將實例方法MyFunc()的native int類型的非托管指針推到棧中,從而將該方法的指針傳到委托的構造函數中;

  由於上面的構造函數存在C#中不支持的函數指針類型void(),所以不能在運行時使用Activator類中的方法創建委托實例,但在委托基類Delegate中存在靜態方法CreateDelegate()調用非托管代碼用於動態創建委托實例,命名空間System.Reflection中的方法信息類MethodInfo的實例方法CreateDelegate()也提供了類似的方式以在運行時動態構建委托實例:

    Type delegateType = typeof(MyDelegate);  //這里以可訪問到的委托類型舉例
    Delegate @delegate = Delegate.CreateDelegate(delegateType, myObj, "MyFunc");
    //@delegate = typeof(MyClass).GetMethod("MyFunc").CreateDelegate(delegateType, myObj);
    //添加其它委托實例
    @delegate = Delegate.Combine(@delegate, otherDelegate);
    //調用委托
    @delegate.DynamicInvoke();
    //當指定的委托類型可訪問時,可以將委托實例顯式轉換為指定的委托類型后使用()或Invoke()正常調用
    //MyDelegate myDelegate = @delegate as MyDelegate;
    //myDelegate();

  對委托實例或方法的+、+=操作實際上也是調用基類Delegate中的靜態方法Combine()並將合成后的委托強制轉換為原類型后返回,-、-=操作則是調用靜態方法Remove();

   

  二、委托的異步調用:通過委托類型的實例方法BeginInvoke開啟子線程並在該子線程中執行委托實例中的方法,以此種方式調用的委托實例中有且只能有一個方法,如果包含多個方法,會拋出異常ArgumentException:

  myDelegate.BeginInvoke(null, null); //其中第一個參數為AsyncCallback類型的回調函數

  如果需要異步調用一個委托實例中方法列表中的所有方法,需要先獲取方法列表,再依次進行異步調用:

  Delegate[] delegates = myDelegate.GetInvocationList();
  for (int i = 0; i < delegates.Length; i++)
  {
    (delegates[i] as MyDelegate).BeginInvoke(null, null);
  }

  三、當調用委托時,如果方法列表中某個方法內引發異常且未在該方法體內捕獲時,該異常將傳遞給委托的調用方,並且不再調用方法列表中的后面的方法,因此在方法體內捕獲異常顯得尤為重要;

  四、泛型中的委托:自定義泛型委托(Generic Delegate),將類型參數用作參數列表或返回值的類型:

  delegate void MyDelegate<T>(T obj); //聲明具有一個類型參數的泛型委托,參數列表中有一個參數
  void MyGenericFunc<T>(T obj) //聲明一個泛型方法,參數列表中有一個參數
  {
    //do…
  }
  void MyFunc(string str)
  {
    //do…
  }
  //聲明泛型委托的實例,指定類型參數為string類型,此時可匹配的方法簽名為void myFunc(string str)
  MyDelegate<string> myDelegate;
  //賦值一個指定類型參數為string的泛型方法
  myDelegate = MyGenericFunc<string>;
  //添加一個參數列表為string類型的具體方法
  myDelegate += MyFunc;

  ※泛型委托同泛型類一樣,需要在實例化時指定類型參數的類型;

  ※泛型委托的實例同具體委托的實例一樣,只需要方法的參數列表和返回值類型相同即可進行匹配,因此不管目標方法是指定了符合要求類型的泛型方法還是具體方法都可以進行匹配;

 


如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的認可是我寫作的最大動力!

作者:Minotauros
出處:https://www.cnblogs.com/minotauros/

本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM