zoukankan      html  css  js  c++  java
  • [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

  • 相关阅读:
    Pandas数据存取
    Pandas统计分析
    (4)awk读取行的细节
    (3)awk命令行结构和语法结构
    安装新版gawk
    (2)BEGIN和END语句块
    (1)AWK快速入门
    shell读取文件的几种方式
    Docker 部署 elk + filebeat
    Linux之关于用户的几个重要配置文件
  • 原文地址:https://www.cnblogs.com/jasongrass/p/12308431.html
Copyright © 2011-2022 走看看