构建邮件服务泛型类结构:解决空引用与泛型参数冗余问题
邮件服务泛型类优化方案
问题背景
我正在构建邮件服务用的泛型类结构,想通过继承抽象类约束主题、模板文件名等属性。核心需求是每个子类通过T InnerObject生成TemplateModel供邮件模板使用。
目前已实现EmailService类及相关结构,但存在两个问题:
InnerObject出现空引用异常SendAsync方法需要传入两个泛型参数<T,Y>,逻辑上没必要暴露第二层泛型参数
期望优化成SendAsync<T>(? innerModel, string toRecipient)形式,当T为AccountCreated(继承自EmailMessageBase<UserRegisteredResponse>)时,编译器能自动约束innerModel必须是UserRegisteredResponse类型。
优化方案
1. 重构抽象基类,从根源避免空引用
调整抽象类的泛型定义,将InnerObject的类型绑定为基类泛型参数,同时通过构造函数强制传入并做空值检查,杜绝空引用:
public abstract class EmailMessageBase<TInner> { public TInner InnerObject { get; } // 子类必须通过构造函数传入InnerObject,且不能为空 protected EmailMessageBase(TInner innerObject) { InnerObject = innerObject ?? throw new ArgumentNullException(nameof(innerObject)); } // 约束子类必须实现的固定属性 public abstract string Subject { get; } public abstract string TemplateFileName { get; } // 定义生成模板模型的抽象逻辑,由子类实现具体转换 public abstract object GenerateTemplateModel(); }
2. 实现具体邮件子类
以AccountCreated为例,绑定对应的内部模型类型UserRegisteredResponse:
public class AccountCreated : EmailMessageBase<UserRegisteredResponse> { public AccountCreated(UserRegisteredResponse innerModel) : base(innerModel) { } public override string Subject => "您的账户已创建成功"; public override string TemplateFileName => "AccountCreatedTemplate.cshtml"; public override object GenerateTemplateModel() { // 根据InnerObject生成模板所需的模型结构 return new { Username = InnerObject.Username, ActivationLink = InnerObject.ActivationLink, ExpirationTime = InnerObject.ExpirationTime }; } }
3. 简化SendAsync方法的泛型参数
通过泛型约束让编译器自动推导内部模型类型,无需手动传入第二层泛型参数:
public class EmailService { // 核心发送方法:约束T必须继承自EmailMessageBase<TInner> public async Task SendAsync<T, TInner>(T emailMessage, string toRecipient) where T : EmailMessageBase<TInner> { // 生成模板模型 var templateModel = emailMessage.GenerateTemplateModel(); // 后续邮件发送流程示例 var templateContent = await LoadTemplateAsync(emailMessage.TemplateFileName); var renderedContent = RenderTemplate(templateContent, templateModel); await SendEmailAsync(toRecipient, emailMessage.Subject, renderedContent); } // 更简化的重载:直接传入内部模型,自动创建邮件实例 public async Task SendAsync<T, TInner>(TInner innerModel, string toRecipient) where T : EmailMessageBase<TInner> { var emailMessage = (T)Activator.CreateInstance(typeof(T), innerModel); await SendAsync(emailMessage, toRecipient); } // 辅助方法示例 private Task<string> LoadTemplateAsync(string templateFileName) { // 实现模板加载逻辑 return Task.FromResult("模板内容"); } private string RenderTemplate(string templateContent, object model) { // 实现模板渲染逻辑 return $"渲染后的内容:{model.ToString()}"; } private Task SendEmailAsync(string to, string subject, string content) { // 实现邮件发送逻辑 Console.WriteLine($"发送邮件到{to},主题:{subject}"); return Task.CompletedTask; } }
4. 调用方式
现在调用时只需指定邮件类型T,编译器会自动校验内部模型的类型合法性:
var emailService = new EmailService(); var userResponse = new UserRegisteredResponse { Username = "test_user", ActivationLink = "https://example.com/activate", ExpirationTime = DateTime.Now.AddHours(24) }; // 方式1:传入已创建的邮件实例 await emailService.SendAsync(new AccountCreated(userResponse), "user@example.com"); // 方式2:直接传入内部模型,自动创建邮件实例 await emailService.SendAsync<AccountCreated, UserRegisteredResponse>(userResponse, "user@example.com");
额外优化:通过工厂类简化实例创建
如果觉得重载方法的泛型参数仍有冗余,可以用静态工厂类统一管理邮件实例的创建:
public static class EmailMessageFactory { public static T Create<T, TInner>(TInner innerModel) where T : EmailMessageBase<TInner> { return (T)Activator.CreateInstance(typeof(T), innerModel); } } // 调用示例 var accountCreatedEmail = EmailMessageFactory.Create<AccountCreated, UserRegisteredResponse>(userResponse); await emailService.SendAsync(accountCreatedEmail, "user@example.com");
内容的提问来源于stack exchange,提问作者Kamil Turowski
相关产品推荐
相关产品推荐

