可以构造函数是asynchronous的吗?

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

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 。 可能有很多(甚至是明显的)边缘情况和原因,我只是想不出来。 我也search周围的解释,但似乎无法find任何。

构造函数与返回构造types的方法非常类似。 而async方法不能返回任何types,它必须是“火和遗忘” void ,或Task

如果typesT的构造函数实际上返回Task<T> ,我想这会很混乱。

如果asynchronous构造函数的行为与async void方法的行为方式相同,那么这种types的构造函数就意味着什么。 构造函数返回后,你应该得到一个完全初始化的对象。 不是在将来某个未定义的点实际正确初始化的对象。 也就是说,如果你很幸运,asynchronous初始化不会失败。

这一切只是一个猜测。 但在我看来,有一个asynchronous构造函数的可能性带来更多的麻烦,而不是它的价值。

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

由于无法构buildasynchronous构造函数,因此我使用静态asynchronous方法来返回由私有构造函数创build的类实例。 这不是优雅的,但它工作正常。

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

你的问题是相当于创build一个文件对象并打开文件。 实际上,在实际使用对象之前,有很多类需要执行两个步骤:create + Initialize(通常称为类似于Open的东西)。

这样做的好处是构造函数可以是轻量级的,而Open函数可以完成困难和耗时的工作。 这个Open函数甚至可以是asynchronous的。

缺点是你必须相信你的类的用户,他会使用你的类的任何其他function之前调用Initialize()。 事实上,如果你想让你的类充分certificate(傻瓜certificate?),你必须检查Initialize()被调用的每个函数。

使之更容易的模式是将构造函数声明为private,并创build一个公共的静态函数来构造对象,并在返回构造的对象之前调用Initialize()。 这样你就知道每个有权访问对象的人都使用了Initialize函数。

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

 public MyClass { public static async Task<MyClass> CreateAsync(...) { MyClass x = new MyClass(); await x.Initialize(...) return x; } // make sure no one but the Create function can call the constructor: private MyClass(){} private async Task Initialize(...) { // do the async things you wanted to do in your async constructor } public async Task<int> MyFunctionAsync(int a, int b) { return await OtherFunctionAsync(a, b); } 

用法如下:

 public async Task<int> SomethingAsync() { MyClass myObject = await MyClass.CreateAsync(...); return await myObject.MyFunctionAsync(4, 7); } 

如果你使构造函数asynchronous,创build一个对象后,你可能会陷入像空值而不是实例对象的问题。 例如;

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

这就是为什么他们不允许这个我猜。

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

我刚刚发布了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团队没有编写这个function。

我相信用正确的语法可以实现,不应该太混乱或容易出错。 我认为斯蒂芬·克莱里的博客文章和其他一些答案隐含地指出,没有任何根本的理由反对它,更重要的是解决了缺乏解决方法。 这些相对简单的解决方法的存在可能是该function尚未实现的原因之一。

我不熟悉async关键字(这是Silverlight特有的还是Visual Studiotesting版的一个新特性?),但我想我可以告诉你为什么你不能这样做。

如果我做:

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

o在下一行代码运行之前,可能不会进行初始化。 你的对象实例化不能被分配,直到你的构造函数完成,并使构造函数asynchronous不会改变,那么什么是重点? 然而,你可以从你的构造函数中调用一个asynchronous方法,然后你的构造函数就可以完成了,而你的exception方法仍然在做任何事情来设置你的对象。

你可以在构造函数里面使用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; } } 

我使用这个简单的技巧。

 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 */ } } 

我会用这样的东西。

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

这与我可以获得的构造函数非常接近。