Can constructors be async?
if you make constructor asynchronous, after creating an object, you may fall into problems like null values instead of instance objects. For instance;
MyClass instance = new MyClass();
instance.Foo(); // null exception here
That's why they don't allow this i guess.
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);
}
Constructor acts very similarly to a method returning the constructed type. And async
method can't return just any type, it has to be either “fire and forget” void
, or Task
.
If the constructor of type T
actually returned Task<T>
, that would be very confusing, I think.
If the async constructor behaved the same way as an async void
method, that kind of breaks what constructor is meant to be. After constructor returns, you should get a fully initialized object. Not an object that will be actually properly initialized at some undefined point in the future. That is, if you're lucky and the async initialization doesn't fail.
All this is just a guess. But it seems to me that having the possibility of an async constructor brings more trouble than it's worth.
If you actually want the “fire and forget” semantics of async void
methods (which should be avoided, if possible), you can easily encapsulate all the code in an async void
method and call that from your constructor, as you mentioned in the question.
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;
}
}