[C#/UI] 使用 await 實現業務對 UI 的控制反轉


背景:WPF/WinForm 桌面程序開發

問題

在涉及到與用戶交互的業務場景下,經常容易在界面的后台代碼(也就是 xxx.xaml.cs)中編寫業務邏輯,在這里調用業務層提供的方法。
如此一來,UI 的后台代碼會變得臃腫,職責不清晰。而且由於與界面的耦合太深,后期修改需求會非常麻煩。

問題出在哪?

UI 應該只是提供基本的用戶交互,不應該成為業務邏輯的控制中心,需要將業務代碼放到獨立的模塊中,業務代碼通過接口來調用 UI,以實現用戶的交互。
控制反轉 是指:應該有業務層代碼調用 UI,而不是 UI 調用業務邏輯代碼。

當然,最開始的調用一般是由 UI 發起的,這里強調的是:流程與邏輯的控制代碼,應該在遠離 UI 的業務層,UI 只負責用戶交互。

改善措施

容易想到的改善辦法是:在 UI 中定義事件,業務層訂閱事件,以獲取用戶操作的結果。
這樣做是可以的,但實際寫起代碼來就會發現,使用事件訂閱的方式,容易造成執行邏輯的割裂,代碼的可讀性會變得很差。

既然要等待用戶操作的結果,除了事件之外,能否實現同步的等待呢?
比如,一個笨辦法就是:寫一個 while(true) 循環,不斷檢測用戶是否完成了操作,如果完成了,就返回操作結果。
這樣就不用使用事件調來調去了,可以同步等待用戶完成操作。

使用 await

當然,while(true) 的方案不會是真實的措施,使用 await 就可以實現這樣的效果。
本質是,一個可等待的對象 awaiter 內部有一個通知機制,當你 await 一個對象之后,就會一直阻塞,等待通知。像不像是對事件的一種封裝?哈哈。

這個通知機制就是 INotifyCompletion Interface (System.Runtime.CompilerServices) | Microsoft Docs

代碼就是:await 用戶操作(); ,如果用戶操作沒有完成,則這里就阻塞。
如此一來,業務邏輯寫起來就會順暢很多。

具體實現原理與方法可以看:
在 WPF/UWP 中實現一個可以用 await 異步等待 UI 交互操作的 Awaiter - walterlv

Demo 分析

Demo:Jasongrass/DemoPark - 碼雲 - 開源中國

使用事件實現的流程控制代碼:

        public void DoFlow()
        {
            _isUnderFlowing = true;
            Step1();
        }

        private void OnUserInputFinished(object sender, string inputContent)
        {
            // 狀態判斷,如有沒有執行 Step1 等。如果狀態判斷OK,則執行 Step2。
            if (!_isUnderFlowing)
            {
                return;
            }
            var precessResult = Step2(inputContent);
            StepEnd();

            // 外部如何拿到 precessResult?
        }

會發現,整個流程被分成了兩部分,而且沒法很好地返回最終處理結果(因為代碼在事件的響應里面)。

使用 awaiter 實現的流程控制代碼:

        public async Task<string> DoFlowAsync()
        {
            Step1();
            var inputContent = await UserInputViewHandler.GetUserInputAsync();
            var precessResult = Step2(inputContent);
            StepEnd();
            return precessResult;
        }

可以在一個函數里面,處理所有的邏輯。

更重要的是,這里還是只有一個 UI 交互的場景,在需要更多的 UI 交互時,如果使用事件的實現方式,代碼理解起來將是一個災難。

核心代碼

UI 部分要支持這種調用當時,需要的核心代碼其實很少。

使用 walterlv 封裝的這個 DispatcherAsyncOperation 類,實現對用戶操作的 awaiter 等待,會很輕松。


基礎原理文章:
在 WPF/UWP 中實現一個可以用 await 異步等待 UI 交互操作的 Awaiter - walterlv

Demo源代碼:
Jasongrass/DemoPark - 碼雲 - 開源中國

原文鏈接:
https://www.cnblogs.com/jasongrass/p/12308431.html


免責聲明!

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



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