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

Minotauros發表於2018-10-17

  一、當我們使用關鍵字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/

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

 

相關文章