C#Async等待,重构了一个非常简单的练习程序,但给出了不同的行为

new*_*erz 1 c# async-await

我正在努力学习异步等待.我正在尝试一个简单的C#Windows窗体应用程序,只有一个文本框和一个按钮.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        const bool USE_GET_STRING_DIRECTLY = true; // just for switching logic on button1_click

        private async void button1_Click(object sender, EventArgs e)
        {

            textBox1.Clear();
            if (USE_GET_STRING_DIRECTLY)
            {
                // Code #1 <<<---------------------
                textBox1.Text += await GetString();
            }
            else
            {
                // Code #2 <<<---------------------
                var s = await GetString();
                textBox1.Text += s;
            }
        }

        async Task<string> GetString()
        {
            await Task.Delay(2000);
            return "Test";
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

我正在使用USE_GET_STRING_DIRECTLY作为一种条件编译来在代码#1和代码#2之间切换.

如果USE_GET_STRING_DIRECTLY为真,则执行代码#1

textBox1.Text += await GetString();
Run Code Online (Sandbox Code Playgroud)

我在500毫秒内按下按钮两次.我会在文本框中看到文本"Test".

现在,我将USE_GET_STRING_DIRECTLY设置为false,将执行Code#2:

var s = await GetString();
textBox1.Text += s;
Run Code Online (Sandbox Code Playgroud)

我在500毫秒内按下按钮两次.我会在文本框中看到文本"TestTest".它打印测试两次.

在我看来,代码#1和#2是相同的,但它们表现出不同的行为.不同之处在于,另一个等同于变量,而另一个等同于Textbox.Text.这可能是什么原因?我对async/await很新,所以我很确定我在这里遗漏了一些东西.

Ser*_*rvy 5

因此,这里的关键点是运算+=符操作数的计算顺序,以及代码中的位置await.

该方式+=操作的工作原理是,它计算出的第一(左)操作数是什么,在这种情况下,该变量textBox1.Text,那么它从该变量获得的值,然后它计算出的第二(右)操作数是什么,这是无论是await GetString或s,取决于段,您正在使用的,那么它结合了两个值,然后设置新值的变量.

这意味着在第一个片段中,您在等待之前解析textBox1.Text为某个值,而在第二个示例中,您将在等待之后解析为其值.GetStringTextBox1.TextGetString

说完所有这些后,我建议不要使用第二个代码段.避免此问题的正确方法是避免在Clear稍后调用然后附加文本.如果要覆盖现有文本而不是附加到文本,则使用=而不是+=设置文本,因为它将设置它,而不是覆盖它.此外,如果您有一个UI事件的异步处理程序,通常在处理程序的开头禁用该UI控件,然后在最后启用它,如果您不希望用户执行该操作(在此在早期的处理程序仍在运行时,按下按钮).有时你想要允许它,但经常(在这里似乎是这种情况)你没有.