在 C# 中使函数非阻塞/异步的简单方法?

Simple means of making a function non-blocking / async in C#?

我有一个按钮 - 当用户点击它时 - 发送一封电子邮件。我希望它立即 return 并在后台发送电子邮件,而无需在处理电子邮件时暂停 UI。我搜索了 async/await/etc,发现了很多不同的方法 - 我正在寻找一个简单的解决方案。我的电子邮件代码:

public void SendEmail(string toAddress, string subject, string body, string code = null) {
    try {

        var fromAddressObj = new MailAddress("noreply@me.com", "Name");
        var toAddressObj = new MailAddress(toAddress, toAddress);
        const string fromPassword = "Password";

        var smtp = new SmtpClient {
            Host = "smtp.office365.com",
            Port = 587,
            EnableSsl = true,
            DeliveryMethod = SmtpDeliveryMethod.Network,
            UseDefaultCredentials = false,
            Credentials = new NetworkCredential(fromAddressObj.Address, fromPassword)
        };
        using (var message = new MailMessage(fromAddressObj, toAddressObj) {
            Subject = subject,
            IsBodyHtml = true

        }) {
            message.Body = body;
            smtp.Send(message);
        }

    } catch (Exception e) {
        Elmah.ErrorSignal.FromCurrentContext().Raise(e);
    }
}

我怎样才能修改这个以便不阻止来电者?

您可以 运行 在任务中使用您的方法而不是等待它:

private void button_Click(object sender, EventArgs e)
{
   Task.Run( () => SendEMail(address, subject, body) );
}

SmtpClient 具有 SendMailAsync,因此使用它而不是 Send 将在发送时解锁 UI 线程,但您仍然可以处理可能发生的任何异常:

private async void button_Click(object sender, EventArgs e)
{
   await SendEmailAsync(address, subject, body)
}

public async Task SendEmailAsync(string toAddress, string subject, string body, string code = null) 
{
    try 
    {
        var fromAddressObj = new MailAddress("noreply@me.com", "Name");
        var toAddressObj = new MailAddress(toAddress, toAddress);
        const string fromPassword = "Password";

        var smtp = new SmtpClient {
            Host = "smtp.office365.com",
            Port = 587,
            EnableSsl = true,
            DeliveryMethod = SmtpDeliveryMethod.Network,
            UseDefaultCredentials = false,
            Credentials = new NetworkCredential(fromAddressObj.Address, fromPassword)
        };
        using (var message = new MailMessage(fromAddressObj, toAddressObj)
        {
            Subject = subject,
            IsBodyHtml = true
        }) 
        {
            message.Body = body;
            await smtp.SendMailAsync(message);
        }
    }
    catch (Exception e) 
    {
        Elmah.ErrorSignal.FromCurrentContext().Raise(e);
    }
}

如果 async 方法的同步部分(await 之前的部分)仍然设法阻塞 UI 线程,您可以将其卸载到 ThreadPool线程:

private async void button_Click(object sender, EventArgs e)
{
   await Task.Run(() => SendEmailAsync(address, subject, body))
}

备注:

  • 不要在没有 await 的情况下启动 Task 除非你处于无法使用 await 的情况(UI 事件处理程序不是其中一种情况)
  • async void 仅适用于 UI 事件处理程序。不要在其他任何地方使用它。