關於C#調用C++動態庫的文章不少,調用動態庫中回調函數的方法也不在少數。但大多數調用回調函數的方法依然保留了C++的語法特色。異步
好比有一段C++的回調函數代碼,爲了表達它的意思,我把註釋也粘貼了進來:函數
/*********************************************************************************************************
** Function name: epcBuzzerAsyncOn
** Descriptions: 本函數使蜂鳴器蜂鳴指定時間
** input parameters: dwOntime 蜂鳴器持續蜂鳴的時間(ms),其中0表示一直蜂鳴
** output parameters: 無
** Returned value: TRUE:成功;FALSE:失敗
** Note: 本函數以異步方式執行,不會被阻塞
*********************************************************************************************************/
EPCBUZZERLIB_API BOOL epcBuzzerAsyncOn (DWORD dwOntime);
/********************************************************************************************************* ** Function name: epcBuzzerSetCallBackFunc ** Descriptions: 設置回調函數指針,當異步的蜂鳴器操做任務完成後,會調用該回調函數通知用戶程序。 ** input parameters: lpfnNotify 回調函數指針,若是是NULL,則表示不須要通知用戶程序。函數類 ** 型爲void (*lpfnNotify)(BOOL bResult),bResult爲執行結果, ** TRUE表示執行成功,FALSE表示執行失敗 ** output parameters: 無 ** Returned value: 無 *********************************************************************************************************/ EPCBUZZERLIB_API VOID epcBuzzerSetCallBackFunc( void (*lpfnNotify)(BOOL));
通常的文章都會教你轉換成以下代碼,這些方法都屬於Buzzer類:this
/// <summary> /// 使蜂鳴器蜂鳴指定時間,異步執行,不會阻塞當前線程. /// </summary> /// <param name="onTime">蜂鳴時間(ms).</param> /// <returns>true:蜂鳴成功,false:蜂鳴失敗</returns> [System.Runtime.InteropServices.DllImport("Lib\\epcBuzzerLib.dll", SetLastError = true)] private static extern bool epcBuzzerAsyncOn(int onTime); /// <summary> /// 先申明一個委託,該委託定義了蜂鳴完成回調函數的指針和數據類型 /// </summary> /// <param name="result">if set to <c>true</c> [result].</param> public delegate void BuzzerHandler(bool result); /// <summary> /// 再設置蜂鳴完成回調函數. /// </summary> /// <param name="handler">The handler.</param> [System.Runtime.InteropServices.DllImport("Lib\\epcBuzzerLib.dll", SetLastError = true)] private static extern void epcBuzzerSetCallBackFunc(BuzzerHandler handler);
而後在主程序中如此調用:spa
//先定義一個知足委託類型的函數 public void BuzzerNotify(bool result) {
//在這裏編寫蜂鳴完成後你想要執行的代碼...我這裏根據result隨便寫幾行。 if(result) { MessageBox.Show("蜂鳴成功"); } else { MessageBox.Show("蜂鳴失敗"); } } //在按鈕事件中調用回調函數 private void onButton_Click(object sender, EventArgs e) { Buzzer.epcBuzzerCallBackFunc(this.BuzzerNotify); Buzzer.epcBuzzerAsyncOn(3000); }
這樣,在蜂鳴器蜂鳴3000ms以後,就會調用BuzzerNotify,並將一個bool結果傳進去。而後你能夠在BuzzerNotify中作你想作的事。線程
雖然這樣也可以解決問題,可是我總感受很彆扭,總以爲個人代碼裏有C++風格,這讓有代碼潔癖的我感受到渾身不自在。因此,我決定重構一下這段代碼,讓它符合C#的風格。指針
重構的思想是這樣的,雖然我不太能徹底理解回調函數這個東西,可是我能隱約感受到它就是一個事件觸發機制,好比上面那段代碼,就能夠理解爲當蜂鳴完成後,就會通知BuzzerNotify,讓它開始執行。只不過BuzzerNotiy是在主函數中定義的一個函數,它不能像事件那樣能夠註冊,你全部想在蜂鳴器完成後作的工做,都必須寫在這個函數裏面。很明顯,如此糟糕的擴展性,徹底不是C#的風格。因此,我要作的,就是將BuzzerNotify變成Buzzer類的一個事件,讓它可使用+=這樣的神器!code
其實作法也是蠻簡單的,我把代碼一貼出來你們立馬就能明白了。如下是Buzzer類的代碼對象
/// <summary> /// 申明蜂鳴完成回調函數指針和數據類型 /// </summary> /// <param name="result">if set to <c>true</c> [result].</param> public delegate void BuzzerHandler(bool result); /// <summary> /// 蜂鳴完成時觸發該事件. /// </summary> public static event BuzzerHandler BuzzingComplete; /// <summary> /// 使蜂鳴器蜂鳴指定時間,異步執行,不會阻塞當前線程. /// </summary> /// <param name="onTime">蜂鳴時間(ms).</param> /// <returns>true:蜂鳴成功,false:蜂鳴失敗</returns> public static bool TurnOn(int time) { Buzzer.BuzzingCompleteSetCallBack(); return Buzzer.epcBuzzerAsyncOn(time); } /// <summary> /// 使蜂鳴器蜂鳴指定時間,異步執行,不會阻塞當前線程. /// </summary> /// <param name="onTime">蜂鳴時間(ms).</param> /// <returns>true:蜂鳴成功,false:蜂鳴失敗</returns> [System.Runtime.InteropServices.DllImport("Lib\\epcBuzzerLib.dll", SetLastError = true)] private static extern bool epcBuzzerAsyncOn(int onTime); /// <summary> /// 定義蜂鳴完成回調函數,用來處理事件.這個方法比較關鍵,它至關於把以前的BuzzerNotify,只不過把它封裝進Buzzer類了。 /// </summary> /// <param name="result">if set to <c>true</c> [result].</param> private static void Event(bool result) { if (BuzzingComplete != null) { BuzzingComplete(result); } } /// <summary> /// 蜂鳴完成回調函數. /// </summary> private static void BuzzingCompleteSetCallBack() { Buzzer.epcBuzzerSetCallBackFunc(Event); } /// <summary> /// 設置蜂鳴完成回調函數. /// </summary> /// <param name="handler">The handler.</param> [System.Runtime.InteropServices.DllImport("Lib\\epcBuzzerLib.dll", SetLastError = true)] private static extern void epcBuzzerSetCallBackFunc(BuzzerHandler handler);
雖然多了不少代碼,但爲了面向對象,這絕對是值得的!
在主函數中能夠如此調用:blog
private void FirstThing(bool result) { MessageBox.Show("Do my first thing"); } private void SecondThing(bool result) { MessageBox.Show("Do my second thing"); } private void onButton_Click(object sender, EventArgs e) { Buzzer.BuzzingComplete += this.FirstThing; Buzzer.BuzzingComplete += this.SecondThing; Buzzer.TurnOn(3000); }
如此一來,主程序中的代碼看上去就舒服多了。值得一提的是,若是你連續點幾回這個OKButton的話,這兩個方法會被重複註冊。事件