【问题标题】:Is this the right way to reconfigure my constructor to use async and await?这是重新配置我的构造函数以使用异步和等待的正确方法吗?
【发布时间】:2022-11-18 00:13:10
【问题描述】:

我已经阅读了几篇文章,但我很难理解这一点。我试图在加载一些数据时保持我的 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;
}

这在我的代码中产生了级联效应。我必须将返回类型更改为任务,并使其他方法异步

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;
}

我收到关于在最后两种方法中没有等待的编译器警告。我真的会等待所有的 Add() 吗?

我只是让依赖注入按照我想要的方式工作,但这一切都级联回我的视图模型,它注入了我的依赖项:

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 简直就是在毁掉我的项目。

【问题讨论】:

  • 为什么把GetPayees和GetCategories改成返回任务?它们不包含任何异步代码?或者这些模型是 Stack Overflow 并且这些方法实际上是异步调用数据库之类的吗?
  • 构造函数不能是异步的,工厂方法可以……但是你不应该尝试在任何构造函数中做 CPU 或 I/O 繁重的工作。您应该在您的视图操作中调用一些异步方法,而不是您的模型构造器。
  • @ProgrammingLlama 我的印象是如果你想在一个方法上使用await,那个方法也需要是async,然后返回一个任务。目前在我的应用程序开发中,我只是创建列表并使用占位符数据,直到我开始实施 SQLite。我想首先充实 UI 和业务逻辑。 :)
  • async 允许您在方法中使用 await。您可以创建一个返回 Task 而不返回 async 的方法。如果您没有什么可等待的,那么您可能不想创建一个 async / 返回 Task 的方法,除非您符合接口或抽象方法。等待返回 Task 的方法也不一定使其异步。如果一个方法实际上没有做一些异步的事情,那么它可能无论如何都会同步运行。

标签: c# asynchronous async-await


【解决方案1】:

async 关键字仅允许使用 await。如果你不需要使用await,那么你就不需要async。

List.Add() 不是异步方法,因此您不会使用await。所以只需删除async关键字;你不需要它。

当 CPU 必须等待外部资源时,异步编程很有用,例如:网络请求、文件存储请求等——任何不在 CPU 或 RAM 中的资源。将对象添加到集合中纯粹是内存操作,因此不适合异步编程。

我认为阅读 Microsoft 在 Asynchronous programming with async and await 上的文档会对您有所帮助。它写得很好。


更新:

既然你说对 Add() 的调用只是 SQL 访问的占位符,那么是的,你会想使用 async 进行 SQL 调用,而且你的做法是正确的:创建一个 public static async 方法,它将为您创建该类的实例。这称为“工厂方法”。你甚至可以更进一步 make the constructor private 这样你就可以强制使用工厂方法:

private DataService() {}

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

如果必须调用 GetPayees() 和 GetCategories() 以使对象处于可用状态,则拥有私有构造函数很重要。

【讨论】:

  • 在我的应用程序的当前状态下,将对象添加到集合只是 SQL 访问的占位符。它会暂停我的用户界面一两秒钟,我不希望这种情况发生。我已经阅读了那个特定的 Microsoft 文档,甚至还阅读了早餐示例。我会再次阅读文档。
  • 如果它是 SQL 访问的占位符,那么是的,这是您想要使用 async/await 的地方。我已经更新了我的答案。
猜你喜欢
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多