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

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


当前回答

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

其他回答

你可以创建一个包装器并注入一个表示构造函数的函子:

class AsyncConstruct<T>
    where T: class
{
    private readonly Task<T> m_construction;
    private T m_constructed;
    public AsyncConstruct(Func<T> createFunc)
    {
        m_constructed = null;
        m_construction = Task.Run(()=>createFunc());
    }

    public T Get()
    {
        if(m_constructed == null)
        {
            m_constructed = m_construction.Result;
        }
        return m_constructed;
    }
}

在构造函数中调用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

构造函数的作用与返回构造类型的方法非常相似。async方法不能返回任何类型,它必须是“触发并忘记”void或Task。

如果类型T的构造函数实际返回Task<T>,我认为这将非常令人困惑。

如果async构造函数的行为与async void方法相同,那就破坏了构造函数的意义。构造函数返回后,您应该得到一个完全初始化的对象。不是一个在将来某个未定义的点上将被正确初始化的对象。也就是说,如果你足够幸运,异步初始化没有失败。

所有这些都只是猜测。但在我看来,使用异步构造函数的可能性带来的麻烦比它的价值要多。

如果你真的想要async void方法的“发射并忘记”语义(如果可能的话应该避免),你可以很容易地将所有代码封装在一个async void方法中,并从你的构造函数调用它,就像你在问题中提到的那样。

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

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

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