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

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。可能有很多(甚至明显的)边缘情况和理由反对它,我只是想不出任何一个。我也四处寻找解释,但似乎找不到任何解释。


当前回答

我会用这样的东西。

 public class MyViewModel
    {
            public MyDataTable Data { get; set; }
            public MyViewModel()
               {
                   loadData(() => GetData());
               }
               private async void loadData(Func<DataTable> load)
               {
                  try
                  {
                      MyDataTable = await Task.Run(load);
                  }
                  catch (Exception ex)
                  {
                       //log
                  }
               }
               private DataTable GetData()
               {
                    DataTable data;
                    // get data and return
                    return data;
               }
    }

这是我能得到的最接近构造函数的值。

其他回答

在构造函数中调用async可能会导致死锁,请参考 http://social.msdn.microsoft.com/Forums/en/winappswithcsharp/thread/0d24419e-36ad-4157-abb5-3d9e6c5dacf1

http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115163.aspx

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

你可以在构造函数中使用Action

 public class ViewModel
    {
        public ObservableCollection<TData> Data { get; set; }
       public ViewModel()
        {              
            new Action(async () =>
            {
                  Data = await GetDataTask();
            }).Invoke();
        }

        public Task<ObservableCollection<TData>> GetDataTask()
        {
            Task<ObservableCollection<TData>> task;
            //Create a task which represents getting the data
            return task;
        }
    }

我不熟悉async关键字(这是针对Silverlight还是Visual Studio测试版的新功能?),但我想我可以告诉你为什么不能这样做。

如果我这样做:

var o = new MyObject();
MessageBox(o.SomeProperty.ToString());

在运行下一行代码之前,O可能无法完成初始化。在构造函数完成之前,不能分配对象的实例化,并且使构造函数异步不会改变这一点,那么重点是什么呢?然而,你可以从你的构造函数调用一个异步方法,然后你的构造函数就可以完成,你就可以在async方法仍然在做它需要做的事情来设置你的对象的时候得到你的实例化。

由于不可能创建异步构造函数,所以我使用一个静态异步方法,该方法返回一个由私有构造函数创建的类实例。这不是很优雅,但它可以工作。

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

    //static async method that behave like a constructor       
    async public static Task<ViewModel> BuildViewModelAsync()  
    {       
        ObservableCollection<TData> tmpData = await GetDataTask();  
        return new ViewModel(tmpData);
    }       

    // private constructor called by the async method
    private ViewModel(ObservableCollection<TData> Data)
    {
        this.Data = Data;   
    }
}