I have a project where I'm trying to populate some data in a constructor:
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; } }
Unfortunately, I'm getting an error:
The modifier
async
is not valid for this item
Of course, if I wrap in a standard method and call that from the constructor:
public async void Foo() { Data = await GetDataTask(); }
it works fine. Likewise, if I use the old inside-out way
GetData().ContinueWith(t => Data = t.Result);
That works too. I was just wondering why we can't call await
from within a constructor directly. There are probably lots of (even obvious) edge cases and reasons against it, I just can't think of any. I've also search around for an explanation, but can't seem to find any.
A simple answer for that: No, we can't! Currently, class constructors do not return types, and an asynchronous method should return a Task type.
You can only use async/await where you can use promises because they are essentially syntax sugar for promises. You can't use promises in a constructor because a constructor must return the object to be constructed, not a promise.
There is no simple way to make constructors async in C#, at least while keeping them as constructors. Constructors can't return a value in the . NET runtime. And the use cases for async constructors can be solved with factory methods that are async instead.
Since performing an asynchronous call to its completion in the constructor is not an option, we can still start a call in the constructor. We'd start it in the constructor, save the unsettled Promise in an instance variable, and then await for its completion in the methods that need it.
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; } }
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With