我有一个项目,我试图在一个构造函数中填充一些数据:

public class ViewModel
{
    public ObservableCollection<TData> Data { get; set; }

    async public ViewModel()
    {
        Data = await GetDataTask();
    }

    public Task<ObservableCollection<TData>> GetDataTask()
    {
        Task<ObservableCollection<TData>> task;

        //Create a task which represents getting the data
        return task;
    }
}

不幸的是,我得到一个错误:

修饰符async对此项无效

当然,如果我包装一个标准方法,并从构造函数调用它:

public async void Foo()
{
    Data = await GetDataTask();
}

它工作得很好。同样,如果我用以前由内而外的方法

GetData().ContinueWith(t => Data = t.Result);

这也有用。我只是想知道为什么我们不能直接从构造函数中调用await。可能有很多(甚至明显的)边缘情况和理由反对它,我只是想不出任何一个。我也四处寻找解释,但似乎找不到任何解释。


当前回答

请点击此语言请求:

https://github.com/dotnet/csharplang/discussions/419

为了拥有一个完全初始化的async对象,每个人都需要编写大量的样板代码,这与c#的趋势完全相反(样板代码更少)。

其他回答

我用这个简单的技巧。

public sealed partial class NamePage
{
  private readonly Task _initializingTask;

  public NamePage()
  {
    _initializingTask = Init();
  }

  private async Task Init()
  {
    /*
    Initialization that you need with await/async stuff allowed
    */
  }
}

其中一些答案涉及创造一种新的公共方法。不这样做,使用Lazy<T>类:

public class ViewModel
{
    private Lazy<ObservableCollection<TData>> Data;

    async public ViewModel()
    {
        Data = new Lazy<ObservableCollection<TData>>(GetDataTask);
    }

    public ObservableCollection<TData> GetDataTask()
    {
        Task<ObservableCollection<TData>> task;

        //Create a task which represents getting the data
        return task.GetAwaiter().GetResult();
    }
}

要使用Data,请使用Data. value。

C# doesn't allow async constructors. Constructors are meant to return fast after some brief initialization. You don't expect and you don't want to wait for an instance i.e. the constructor to return. Therefore, even if async constructors were possible, a constructor is not a place for long-running operations or starting background threads. The only purpose of a constructor is initialization of instance or class members to a default value or the captured constructor parameters. You always create the instance and then call DoSomething() on this instance. Async operations are no exception. You always defer expensive initialization of members.

有一些解决方案可以避免对异步构造函数的需求。

使用Lazy<T>或AsyncLazy<T>的简单替代解决方案(需要通过NuGet包管理器安装Microsoft.VisualStudio.Threading包)。Lazy<T>允许延迟实例化或昂贵资源的分配。

public class OrderService
{
  public List<object> Orders => this.OrdersInitializer.GetValue();
  private AsyncLazy<List<object>> OrdersInitializer { get; }

  public OrderService()
    => this.OrdersInitializer = new AsyncLazy<List<object>>(InitializeOrdersAsync, new JoinableTaskFactory(new JoinableTaskContext()));

  private async Task<List<object>> InitializeOrdersAsync()
  {
    await Task.Delay(TimeSpan.FromSeconds(5));
    return new List<object> { 1, 2, 3 };
  }
}

public static void Main()
{
  var orderService = new OrderService();

  // Trigger async initialization
  orderService.Orders.Add(4);
}

可以使用方法而不是属性公开数据

public class OrderService
{
  private List<object> Orders { get; set; }

  public async Task<List<object>> GetOrdersAsync()
  {
    if (this.Orders == null)
    {
      await Task.Delay(TimeSpan.FromSeconds(5));
      this.Orders = new List<object> { 1, 2, 3 };
    }
    return this.Orders;
  }
}

public static async Task Main()
{
  var orderService = new OrderService();

  // Trigger async initialization
  List<object> orders = await orderService.GetOrdersAsync();
}

使用InitializeAsync方法,在使用实例之前必须调用该方法

public class OrderService
{
  private List<object> orders;
  public List<object> Orders 
  { 
    get
    {
      if (!this.IsInitialized)
      {
        throw new InvalidOperationException(); 
      }
      return this.orders;
    }
    private set
    {
      this.orders = value;
    }
  }

  public bool IsInitialized { get; private set; }

  public async Task<List<object>> InitializeAsync()
  {
    if (this.IsInitialized)
    {
      return;
    }

    await Task.Delay(TimeSpan.FromSeconds(5));
    this.Orders = new List<object> { 1, 2, 3 };
    this.IsInitialized = true;
  }
}

public static async Task Main()
{
  var orderService = new OrderService();

  // Trigger async initialization
  await orderService.InitializeAsync();
}

通过将昂贵的参数传递给构造函数来实例化实例

public class OrderService
{
  public List<object> Orders { get; }

  public async Task<List<object>> OrderService(List<object> orders)
    => this.Orders = orders;
}

public static async Task Main()
{
  List<object> orders = await GetOrdersAsync();

  // Instantiate with the result of the async operation
  var orderService = new OrderService(orders);
}

private static async Task<List<object>> GetOrdersAsync()
{
  await Task.Delay(TimeSpan.FromSeconds(5));
  return new List<object> { 1, 2, 3 };
}

使用工厂方法和私有构造函数

public class OrderService
{
  public List<object> Orders { get; set; }

  private OrderServiceBase()  
    => this.Orders = new List<object>();

  public static async Task<OrderService> CreateInstanceAsync()
  {
    var instance = new OrderService();
    await Task.Delay(TimeSpan.FromSeconds(5));
    instance.Orders = new List<object> { 1, 2, 3 };
    return instance;
  }
}

public static async Task Main()
{
  // Trigger async initialization  
  OrderService orderService = await OrderService.CreateInstanceAsync();
}

在这种特殊情况下,需要一个viewModel来启动任务,并在任务完成时通知视图。一个“async属性”,而不是一个“async构造函数”。

我刚刚发布了AsyncMVVM,它正好解决了这个问题(以及其他问题)。如果你使用它,你的ViewModel将变成:

public class ViewModel : AsyncBindableBase
{
    public ObservableCollection<TData> Data
    {
        get { return Property.Get(GetDataAsync); }
    }

    private Task<ObservableCollection<TData>> GetDataAsync()
    {
        //Get the data asynchronously
    }
}

奇怪的是,它支持Silverlight。:)

您的问题类似于创建文件对象并打开文件。事实上,有很多类在实际使用对象之前必须执行两个步骤:create + Initialize(通常称为类似于Open的东西)。

这样做的好处是构造函数可以是轻量级的。如果需要,可以在实际初始化对象之前更改一些属性。设置完所有属性后,将调用Initialize/Open函数来准备要使用的对象。这个Initialize函数可以是异步的。

缺点是您必须相信类的用户在使用类的任何其他函数之前会调用Initialize()。事实上,如果你想让你的类完全证明(傻瓜证明?),你必须检入Initialize()被调用的每个函数。

简化此操作的模式是将构造函数声明为私有,并创建一个公共静态函数,该函数将构造对象,并在返回构造的对象之前调用Initialize()。这样,您就会知道有权访问该对象的每个人都使用了Initialize函数。

该示例显示了一个模仿所需异步构造函数的类

public MyClass
{
    public static async Task<MyClass> CreateAsync(...)
    {
        MyClass x = new MyClass();
        await x.InitializeAsync(...)
        return x;
    }

    // make sure no one but the Create function can call the constructor:
    private MyClass(){}

    private async Task InitializeAsync(...)
    {
        // do the async things you wanted to do in your async constructor
    }

    public async Task<int> OtherFunctionAsync(int a, int b)
    {
        return await ... // return something useful
    }

使用方法如下:

public async Task<int> SomethingAsync()
{
    // Create and initialize a MyClass object
    MyClass myObject = await MyClass.CreateAsync(...);

    // use the created object:
    return await myObject.OtherFunctionAsync(4, 7);
}