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

MAUI应用中使用async/await重构构造函数的方式是否正确?

MAUI异步初始化数据服务与ViewModel的问题

我读了不少文章,但还是搞不懂。我想在MAUI应用加载数据时保持UI可响应。

我原本想这么写代码:

async public DataService()
{
    //initialize stuff here
    await this.GetPayees();
    await this.GetCategories();
    return;
}

后来知道不能有异步构造函数,就重新设计了初始化方式:

public DataService()
{
    //Take this out here
    //this.GetPayees();
    //this.GetCategories();
    return;
}

async public static Task<DataService> BuildDataServiceAsync()
{
    //await them here
    var dataService = new DataService();
    await dataService.GetPayees();
    await dataService.GetCategories();
    return dataService;
}

这导致连锁反应,我得把很多方法改成异步,返回Task类型:

async public Task<List<Payee>> GetPayees()
{
    //Load arbitrary data, 
    if(Payees.Count != 0) return Payees;
    Payees.Add(new Payee { Id = 0, Name = "Food Lion", DefaultCategoryId = 0, DefaultIsCredit = false });
    Payees.Add(new Payee { Id = 1, Name = "Work Incorporated", DefaultCategoryId = 1, DefaultIsCredit = true });
    Payees.Add(new Payee { Id = 2, Name = "Hardees", DefaultCategoryId = 3, DefaultIsCredit = false });
    Payees.Add(new Payee { Id = 3, Name = "Wal-Mart", DefaultCategoryId = 5, DefaultIsCredit = false });
    Payees.Add(new Payee { Id = 4, Name = "Aldis", DefaultCategoryId = 0, DefaultIsCredit = false });
    Payees.Add(new Payee { Id = 5, Name = "McDonalds", DefaultCategoryId = 3, DefaultIsCredit = false });
    Payees.Add(new Payee { Id = 6, Name = "Harris Teeter", DefaultCategoryId = 0, DefaultIsCredit = false });
    Payees.Add(new Payee { Id = 7, Name = "Shoe Show", DefaultCategoryId = 2, DefaultIsCredit = false });
    Payees.Add(new Payee { Id = 8, Name = "Capital One", DefaultCategoryId = 4, DefaultIsCredit = false });
    Payees.Add(new Payee { Id = 9, Name = "Dicks Sporting Goods", DefaultCategoryId = 6, DefaultIsCredit = false });
    Payees.Add(new Payee { Id = 10, Name = "Amazon", DefaultCategoryId = 7, DefaultIsCredit = false });
    return Payees;
}

async public Task<List<Category>> GetCategories()
{
    if(Categories.Count != 0) return Categories;
    Categories.Add(new Category { Id = 0, Name = "Groceries" });
    Categories.Add(new Category { Id = 1, Name = "Paycheck" });
    Categories.Add(new Category { Id = 2, Name = "Shoes" });
    Categories.Add(new Category { Id = 3, Name = "Fast Food" });
    Categories.Add(new Category { Id = 4, Name = "Credit Card" });
    Categories.Add(new Category { Id = 5, Name = "Supplies" });
    Categories.Add(new Category { Id = 6, Name = "Recreation" });
    Categories.Add(new Category { Id = 7, Name = "Grocery" });
    Categories.Add(new Category { Id = 8, Name = "Gross" });
    Categories.Add(new Category { Id = 9, Name = "Grass" });
    return Categories;
}

现在编译器警告这两个方法里没有await语句,难道我连Add()操作都要加await吗?

刚把依赖调顺,结果ViewModel也受影响了,它注入了这些服务:

public PayeesViewModel(DataService dataService, NavigationService navigationService, ValidationService validationService)
{
    this.dataService = dataService;
    this.navigationService = navigationService;
    this.validationService = validationService;
    Payees = await dataService.GetPayees();
    Categories = await dataService.GetCategories();

    for(int x = 0; x < Payees.Count; x++)
    {
        PayeeDisplay.Add(new PayeeDisplay
        {
            Id = Payees[x].Id,
            Name = Payees[x].Name,
            DefaultCategory = Categories.Find(c => c.Id.Equals(Payees[x].DefaultCategoryId)).Name,
            DefaultCategoryId = Payees[x].DefaultCategoryId,
            DefaultIsCredit = Payees[x].DefaultIsCredit
        });
    }
}

我是不是方法用错了?搞个async/await快把项目搞崩了。


解决方案

1. 消除无await的异步方法警告

你现在的GetPayees和GetCategories全是同步代码,完全没必要加async关键字。只有当方法内部需要调用真正的异步操作(比如从API、数据库读取数据)时,才需要改成异步方法。现在直接把方法改回同步:

public List<Payee> GetPayees()
{
    if(Payees.Count != 0) return Payees;
    // 原Add代码保持不变
    return Payees;
}

public List<Category> GetCategories()
{
    if(Categories.Count != 0) return Categories;
    // 原Add代码保持不变
    return Categories;
}

对应的初始化方法也可以简化,因为调用同步方法不需要await:

public static DataService BuildDataService()
{
    var dataService = new DataService();
    dataService.GetPayees();
    dataService.GetCategories();
    return dataService;
}

等以后需要接入异步数据源时,再把这些方法改成async Task<T>,并在内部用await调用异步API即可。

2. ViewModel的异步初始化处理

构造函数里不能直接用await,有两种常用解决办法:

方法一:新增异步初始化方法

给ViewModel添加一个专门的初始化方法,在ViewModel创建后调用:

public PayeesViewModel(DataService dataService, NavigationService navigationService, ValidationService validationService)
{
    this.dataService = dataService;
    this.navigationService = navigationService;
    this.validationService = validationService;
}

public async Task InitializeAsync()
{
    Payees = dataService.GetPayees(); // 以后改成异步操作时,这里加await
    Categories = dataService.GetCategories();

    foreach(var payee in Payees)
    {
        var category = Categories.Find(c => c.Id == payee.DefaultCategoryId);
        PayeeDisplay.Add(new PayeeDisplay
        {
            Id = payee.Id,
            Name = payee.Name,
            DefaultCategory = category?.Name ?? string.Empty,
            DefaultCategoryId = payee.DefaultCategoryId,
            DefaultIsCredit = payee.DefaultIsCredit
        });
    }
}

然后在MAUI页面的OnAppearing生命周期方法中调用这个初始化方法:

protected override async void OnAppearing()
{
    base.OnAppearing();
    if (BindingContext is PayeesViewModel vm)
    {
        await vm.InitializeAsync();
    }
}

方法二:使用异步工厂方法

如果希望ViewModel创建时就完成初始化,可以仿照DataService的做法,用静态异步工厂方法:

// 把构造函数设为私有,强制通过工厂方法创建实例
private PayeesViewModel(DataService dataService, NavigationService navigationService, ValidationService validationService)
{
    this.dataService = dataService;
    this.navigationService = navigationService;
    this.validationService = validationService;
}

public static async Task<PayeesViewModel> CreateAsync(DataService dataService, NavigationService navigationService, ValidationService validationService)
{
    var vm = new PayeesViewModel(dataService, navigationService, validationService);
    vm.Payees = dataService.GetPayees();
    vm.Categories = dataService.GetCategories();

    foreach(var payee in vm.Payees)
    {
        var category = vm.Categories.Find(c => c.Id == payee.DefaultCategoryId);
        vm.PayeeDisplay.Add(new PayeeDisplay
        {
            Id = payee.Id,
            Name = payee.Name,
            DefaultCategory = category?.Name ?? string.Empty,
            DefaultCategoryId = payee.DefaultCategoryId,
            DefaultIsCredit = payee.DefaultIsCredit
        });
    }
    return vm;
}

这种方式需要在依赖注入注册或ViewModel创建的地方,调用CreateAsync方法来获取实例。

3. 保持UI响应的核心

如果以后数据加载是真正的异步操作(比如网络请求、数据库查询),一定要用await,这样不会阻塞UI线程。现在你的数据是本地模拟的同步代码,本身就不会卡UI,没必要强行套异步的壳。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 20:40:26