You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

构建邮件服务泛型类结构:解决空引用与泛型参数冗余问题

邮件服务泛型类优化方案

问题背景

我正在构建邮件服务用的泛型类结构,想通过继承抽象类约束主题、模板文件名等属性。核心需求是每个子类通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 22:38:13