Loading

[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

posted @ 2020-02-14 18:17  J.晒太阳的猫  阅读(660)  评论(1编辑  收藏  举报