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

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


当前回答

我只是想知道为什么我们不能直接从构造函数中调用await。

我相信简短的答案很简单:因为. net团队还没有编写这个特性。

我相信使用正确的语法可以实现这一点,而且不应该太令人困惑或容易出错。我认为Stephen Cleary的博客文章和其他几个答案已经含蓄地指出,没有根本的理由反对它,而且更重要的是——用变通方法解决了这个不足。这些相对简单的变通方法的存在可能是这个特性(还)没有实现的原因之一。

其他回答

如果你让构造函数是异步的,在创建对象之后,你可能会遇到像空值而不是实例对象这样的问题。例如;

MyClass instance = new MyClass();
instance.Foo(); // null exception here

我猜这就是他们不允许这样做的原因。

在这种特殊情况下,需要一个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。:)

我只是想知道为什么我们不能直接从构造函数中调用await。

我相信简短的答案很简单:因为. net团队还没有编写这个特性。

我相信使用正确的语法可以实现这一点,而且不应该太令人困惑或容易出错。我认为Stephen Cleary的博客文章和其他几个答案已经含蓄地指出,没有根本的理由反对它,而且更重要的是——用变通方法解决了这个不足。这些相对简单的变通方法的存在可能是这个特性(还)没有实现的原因之一。

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

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

我会用这样的东西。

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

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