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

C#中如何在构造函数内进行异步调用?最佳实践探讨

C# Shipping类异步初始化的最佳实现方案

针对你提出的Shipping类设计需求,首先明确:构造函数内直接发起未等待的异步调用是不可取的——这种写法会导致Size属性处于不确定状态,调用依赖该属性的方法时极可能引发空引用或业务逻辑错误,且无法追踪初始化是否成功、是否出现异常。

结合你的需求(保留Shipping类对尺寸的更新职责、避免冗余实现),异步工厂方法是最优方案,以下是具体实现思路和代码示例:

核心思路

  1. 禁止外部直接通过构造函数创建Shipping实例,将构造函数设为私有;
  2. 提供静态异步工厂方法CreateAsync,在该方法内完成数据库异步调用,确保Size属性初始化完成后再返回实例;
  3. 在Shipping类内封装尺寸更新逻辑,支持产品变更时主动刷新尺寸。

代码实现

首先假设IProduct接口和IDbService定义如下(可根据实际调整):

public interface IProduct
{
    string ProductReference { get; }
    // 其他产品属性(如名称、类型等)
}

public interface IDbService
{
    Task<double> GetProductSize(string productReference);
}

Shipping类的实现:

public class Shipping
{
    private readonly IDbService _dbService;
    public IProduct Product { get; private set; }
    public double Size { get; private set; }

    // 私有构造函数:仅内部使用,确保实例必须通过工厂方法创建
    private Shipping(IProduct product, IDbService dbService)
    {
        Product = product ?? throw new ArgumentNullException(nameof(product));
        _dbService = dbService ?? throw new ArgumentNullException(nameof(dbService));
    }

    // 异步工厂方法:完成初始化并返回就绪的Shipping实例
    public static async Task<Shipping> CreateAsync(IProduct product, IDbService dbService)
    {
        var shipping = new Shipping(product, dbService);
        await shipping.UpdateSizeAsync(); // 同步完成尺寸初始化
        return shipping;
    }

    // 更新尺寸方法:封装数据库调用逻辑,支持产品变更后刷新
    public async Task UpdateSizeAsync()
    {
        Size = await _dbService.GetProductSize(Product.ProductReference);
    }

    // 依赖Size的业务方法:此时Size已确保就绪
    public double CalculateShippingCost()
    {
        // 示例计算逻辑,可根据实际业务调整
        return Size * 0.8;
    }

    // 可选:支持变更产品并同步更新尺寸
    public async Task SetProductAsync(IProduct newProduct)
    {
        Product = newProduct ?? throw new ArgumentNullException(nameof(newProduct));
        await UpdateSizeAsync();
    }
}

方案优势

  • 状态确定:调用者通过await Shipping.CreateAsync(...)获取实例时,Size已完成初始化,无需担心属性未就绪的问题;
  • 职责清晰:Shipping类完全负责尺寸的获取与更新,符合你“保留设置职责”的需求,产品变更时只需调用SetProductAsync即可自动刷新尺寸;
  • 错误可控:数据库调用失败时,CreateAsync会直接抛出异常,调用者可统一捕获处理,避免静默失败;
  • 轻量简洁:相比Builder模式,无需额外定义Builder类,代码量更少,维护成本更低。

其他方案对比

  • 提前获取尺寸再创建实例:将数据库调用移到外部,虽然简单,但会导致Shipping类失去对尺寸逻辑的控制权,产品变更时需外部重复处理,不符合职责单一原则;
  • 构造函数内异步调用:如你提供的代码,存在状态不确定、线程安全、错误无法追踪等问题,属于反模式,不推荐;
  • Builder模式:若仅用于初始化,确实冗余,且会增加代码复杂度,仅在对象构造逻辑极复杂时适用。

内容的提问来源于stack exchange,提问作者Aurel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 18:00:53