C#使用委托实现异步发送邮件及回调处理

一颗松果
一颗松果
发布时间:2026-10-13 | 3人浏览过
原创

在C#项目开发中,同步发送邮件常导致主线程阻塞,影响系统响应速度。本文介绍利用 System.Net.Mail 命名空间下的 SmtpClient.SendAsync 方法,配合 Action<string> 委托回调机制,实现非阻塞式邮件发送。该方案将邮件发送组件与业务日志记录分离,确保异步任务结束后自动触发后续操作。

问题背景与解决方案

在重构旧项目时,发现原有的邮件发送功能采用同步模式(Send),导致在等待邮件发送期间程序无法执行其他逻辑。由于邮件发送后的结果通常仅需写入日志,对核心业务流无直接影响,因此最适合的做法是将其改为异步执行。

难点在于:邮件发送类库通常独立于主业务模块,无法直接在主程序中插入日志代码。解决方案是利用C#的委托特性,在发送邮件的方法中接受一个回调函数作为参数。当异步发送完成(无论成功或失败)时,自动调用该回调函数来处理日志记录或其他收尾工作。

环境准备

  • 开发语言: C# (.NET Framework 或 .NET Core/5+)
  • 关键类库: System.Net.Mail (用于构建邮件和客户端), System.Threading (用于延时模拟)
  • 前置条件: 需要有效的SMTP服务器地址、账号、密码及接收者邮箱地址

第1步:定义邮件辅助类 MailHelper

目的:封装SMTP客户端配置与邮件消息体构建,提供统一的异步发送接口。

动作:

  1. 定义属性:配置 SmtpServer, UserName, Pwd, Subject, Body 等SMTP连接参数与邮件内容。
  2. 构建 MailMessage:通过 GetClient 属性解析收件人列表(支持逗号分隔多个地址),设置发件人、主题、正文编码(UTF8)及优先级。
  3. 构建 SmtpClient:通过 GetSmtpClient 属性实例化客户端,配置主机、端口、凭据及SSL启用状态。

第2步:实现 SendAsync 异步发送逻辑

目的:启动异步邮件发送进程,并注册完成时的回调处理。

入口:public void SendAsync(string subject, string body, string to, Action<string> _actinCompletedCallback)

重要参数:

  • to: 接收者邮箱,多个可用逗号分隔。
  • _actinCompletedCallback: 发送完成后执行的回调函数,接收字符串类型的结果描述。

操作细节:

  1. 初始化 SmtpClient 和 MailMessage 对象。
  2. 绑定 SendCompleted 事件到内部处理方法 SendCompletedCallback。
  3. 调用 smtpClient.SendAsync(mailMessage, "true") 发起异步请求。第二个参数 userToken 传为 "true",用于在回调中标识请求来源。
  4. 捕获异常抛出,并在 Finally 块中释放本地引用。

第3步:处理异步完成回调 SendCompletedCallback

目的:根据异步操作的结果状态,决定调用何种业务回调。

动作:

  1. 检查取消或错误:若 e.Cancelled 为真,返回“异步操作取消”;若 e.Error 不为空,格式化错误信息。
  2. 成功处理:若无错误或取消,直接使用 UserState(即传入的 "true")作为成功标识。
  3. 执行委托:调用之前注册的 actionSendCompletedCallback(message),将结果状态传递给外部业务逻辑(如写入日志)。
using System;
using System.Net.Mail;

namespace SendAsyncEmailTest
{
    class Program
    {
        const string dateFormat = "yyyy-MM-dd :HH:mm:ss:ffffff";

        static void Main(string[] args)
        {
            Console.WriteLine("开始异步发送邮件,时间:" + DateTime.Now.ToString(dateFormat));
            // 调用异步发送方法,传入自定义的完成回调函数 emailCompleted
            new MailHelper().SendAsync("Send Async Email Test", "This is Send Async Email Test", "recipient@example.com", emailCompleted);
            
            Console.WriteLine("邮件正在异步发送,时间:" + DateTime.Now.ToString(dateFormat));
            Console.ReadKey();
            Console.WriteLine();
        }

        /// <summary>
        /// 邮件发送后的回调方法
        /// </summary>
        static void emailCompleted(string message)
        {
            // 模拟日志写入前的处理延时
            System.Threading.Thread.Sleep(1000);
            Console.WriteLine();
            Console.WriteLine("邮件发送结果:\r\n" + (message == "true" ? "邮件发送成功" : "邮件发送失败") + ",时间:" + DateTime.Now.ToString(dateFormat));
            // 在此处添加具体的日志写入逻辑
        }
    }

    public class MailHelper
    {
        private readonly int smtpPort = 25;
        readonly string SmtpServer = "smtp.example.com"; // 请替换为实际SMTP服务器
        private readonly string UserName = "support@example.com"; // 请替换为用户名
        readonly string Pwd = "your_password"; // 请替换为密码
        private readonly string AuthorName = "Author";

        public string Subject { get; set; }
        public string Body { get; set; }
        public string Tos { get; set; }
        public bool EnableSsl { get; set; }

        MailMessage GetClient
        {
            get
            {
                if (string.IsNullOrEmpty(Tos)) return null;
                MailMessage mailMessage = new MailMessage();
                foreach (string _str in Tos.Split(','))
                {
                    mailMessage.To.Add(_str);
                }
                mailMessage.From = new System.Net.Mail.MailAddress(UserName, AuthorName);
                mailMessage.Subject = Subject;
                mailMessage.Body = Body;
                mailMessage.IsBodyHtml = true;
                mailMessage.BodyEncoding = System.Text.Encoding.UTF8;
                mailMessage.SubjectEncoding = System.Text.Encoding.UTF8;
                mailMessage.Priority = System.Net.Mail.MailPriority.High;
                return mailMessage;
            }
        }

        SmtpClient GetSmtpClient
        {
            get
            {
                return new SmtpClient
                {
                    UseDefaultCredentials = false,
                    Credentials = new System.Net.NetworkCredential(UserName, Pwd),
                    DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network,
                    Host = SmtpServer,
                    Port = smtpPort,
                    EnableSsl = EnableSsl,
                };
            }
        }

        // 存储回调委托
        Action<string> actionSendCompletedCallback = null;

        /// <summary>
        /// 使用异步发送邮件
        /// </summary>
        /// <param name="subject">主题</param>
        /// <param name="body">内容</param>
        /// <param name="to">接收者,以,分隔多个接收者</param>
        /// <param name="_actinCompletedCallback">邮件发送后的回调方法</param>
        public void SendAsync(string subject, string body, string to, Action<string> _actinCompletedCallback)
        {
            if (string.IsNullOrEmpty(to)) return;
            Tos = to;
            SmtpClient smtpClient = GetSmtpClient;
            MailMessage mailMessage = GetClient;
            if (smtpClient == null || mailMessage == null) return;

            Subject = subject;
            Body = body;
            EnableSsl = false;

            // 保存回调引用
            actionSendCompletedCallback = _actinCompletedCallback;
            smtpClient.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);

            try
            {
                smtpClient.SendAsync(mailMessage, "true");
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
            finally
            {
                smtpClient = null;
                mailMessage = null;
            }
        }

        /// <summary>
        /// 异步操作完成后执行回调方法
        /// </summary>
        private void SendCompletedCallback(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            if (actionSendCompletedCallback == null) return;
            string message = string.Empty;

            if (e.Cancelled)
            {
                message = "异步操作取消";
            }
            else if (e.Error != null)
            {
                message = (string.Format("UserState:{0},Message:{1}", (string)e.UserState, e.Error.ToString()));
            }
            else
            {
                message = (string)e.UserState;
            }

            // 执行外部传入的回调
            actionSendCompletedCallback(message);
        }
    }
}

运行效果验证

编译运行上述代码后,控制台将首先输出“开始异步发送邮件”与“邮件正在异步发送”,表明主线程未被阻塞。随后,在经过短暂的网络传输延迟后,将在控制台打印“邮件发送结果”及对应的时间戳,并显示成功或失败的状态。这证明了异步机制与回调函数的正确配合。

注意事项与排错

  • 依赖项差异:如果在 .NET Core / .NET 5+ 环境中使用,System.Net.Mail 已移除,需使用 MailKit 等第三方库,其异步 API 基于 Task-based Async Pattern (TAP),用法略有不同(需使用 SendAsync 并 await 返回值),但回调解耦的思想依然适用。
  • 认证信息:示例中的 SMTP 服务器、用户名和密码仅为占位符,实际使用时请务必替换为邮箱服务商提供的有效凭证及正确的 SMTP 端口(通常为 465 SSL 或 587 STARTTLS)。
  • 异常捕获:生产环境中建议在 emailCompleted 回调内增加更完善的日志记录机制,以便排查网络超时或认证失败的具体原因。

以上就是C#使用委托实现异步发送邮件及回调处理的详细内容,更多关于C#异步编程与邮件服务集成资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表