I have a project where I\'m trying to populate some data in a constructor:
public class ViewModel
{
public ObservableCollection Data { get;
Since it is not possible to make an async constructor, I use a static async method that returns a class instance created by a private constructor. This is not elegant but it works ok.
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;
}
}
I was just wondering why we can't call
await
from within a constructor directly.
I believe the short answer is simply: Because the .Net team has not programmed this feature.
I believe with the right syntax this could be implemented and shouldn't be too confusing or error prone. I think Stephen Cleary's blog post and several other answers here have implicitly pointed out that there is no fundamental reason against it, and more than that - solved that lack with workarounds. The existence of these relatively simple workarounds is probably one of the reasons why this feature has not (yet) been implemented.
calling async in constructor maybe cause deadlock, please refer to 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
Your problem is comparable to the creation of a file object and opening the file. In fact there are a lot of classes where you have to perform two steps before you can actually use the object: create + Initialize (often called something similar to Open).
The advantage of this is that the constructor can be lightweight. If desired, you can change some properties before actually initializing the object. When all properties are set, the Initialize
/Open
function is called to prepare the object to be used. This Initialize
function can be async.
The disadvantage is that you have to trust the user of your class that he will call Initialize()
before he uses any other function of your class. In fact if you want to make your class full proof (fool proof?) you have to check in every function that the Initialize()
has been called.
The pattern to make this easier is to declare the constructor private and make a public static function that will construct the object and call Initialize()
before returning the constructed object. This way you'll know that everyone who has access to the object has used the Initialize
function.
The example shows a class that mimics your desired async constructor
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
}
Usage will be as follows:
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);
}
Some of the answers involve creating a new public
method. Without doing this, use the Lazy<T>
class:
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();
}
}
To use Data
, use Data.Value
.
In this particular case, a viewModel is required to launch the task and notify the view upon its completion. An "async property", not an "async constructor", is in order.
I just released AsyncMVVM, which solves exactly this problem (among others). Should you use it, your ViewModel would become:
public class ViewModel : AsyncBindableBase
{
public ObservableCollection<TData> Data
{
get { return Property.Get(GetDataAsync); }
}
private Task<ObservableCollection<TData>> GetDataAsync()
{
//Get the data asynchronously
}
}
Strangely enough, Silverlight is supported. :)