Могут ли конструкторы быть асинхронными?

У меня есть проект Silverlight, где я пытаюсь заполнить некоторые данные в конструкторе:

public class ViewModel { public ObservableCollection Data { get; set; } async public ViewModel() { Data = await GetDataTask(); } public Task<ObservableCollection> GetDataTask() { Task<ObservableCollection> 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 от конструктора напрямую. Вероятно, есть много (даже очевидных) краевых случаев и причин, против которых я просто не могу думать. Я также искал объяснения, но, похоже, не нашел.

Конструктор действует очень похоже на метод, возвращающий построенный тип. async метод не может возвращать какой-либо тип, он должен быть либо «огнем, либо забыть», либо « Task .

Если конструктор типа T фактически вернул Task , это было бы очень запутанным, я думаю.

Если конструктор async вел себя так же, как метод async void , такой разрыв прерывается, каким должен быть конструктор. После возврата конструктора вы должны получить полностью инициализированный объект. Не объект, который будет фактически правильно инициализирован в какой-то неопределенной точке в будущем. То есть, если вам повезет, и инициализация async не завершится неудачей.

Все это просто догадка. Но мне кажется, что наличие возможности асинхронного конструктора приносит больше проблем, чем того стоит.

Если вы действительно хотите использовать семантику «огонь и забыть» методов async void (чего следует избегать, если это возможно), вы можете легко инкапсулировать весь код в методе async void и вызвать это из своего конструктора, как вы упомянули в вопросе ,

Поскольку невозможно создать конструктор async, я использую статический метод async, который возвращает экземпляр classа, созданный частным конструктором. Это не изящно, но работает нормально.

  public class ViewModel { public ObservableCollection Data { get; set; } //static async method that behave like a constructor async public static Task BuildViewModelAsync() { ObservableCollection tmpData = await GetDataTask(); return new ViewModel(tmpData); } // private constructor called by the async method private ViewModel(ObservableCollection Data) { this.Data=Data; } } 

Ваша проблема сопоставима с созданием файлового объекта и открытием файла. На самом деле есть много classов, где вам нужно выполнить два шага, прежде чем вы сможете реально использовать объект: create + Initialize (часто называемый чем-то похожим на Open).

Преимущество этого заключается в том, что конструктор может быть легким. При желании вы можете изменить некоторые свойства до фактической инициализации объекта. Когда все свойства установлены, вызывается функция Initialize / Open для подготовки объекта, который будет использоваться. Эта функция Initialize может быть асинхронной.

Недостатком является то, что вы должны доверять пользователю своего classа, который он назовет Initialize() прежде чем он будет использовать любую другую функцию вашего classа. На самом деле, если вы хотите сделать свой class полным доказательством (доказательство дурака?), Вам нужно проверить каждую функцию, которую вызывается Initialize() .

Шаблон, чтобы сделать это проще, – объявить конструктор private и сделать публичную статическую функцию, которая будет строить объект, и вызвать Initialize() перед возвратом построенного объекта. Таким образом, вы узнаете, что все, кто имеет доступ к объекту, использовали функцию Initialize .

В этом примере показан class, который имитирует ваш желаемый конструктор async

 public MyClass { public static async Task 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 OtherFunctionAsync(int a, int b) { return await OtherFunctionAsync(a, b); } 

Использование будет следующим:

 public async Task SomethingAsync() { // Create and initialize a MyClass object MyClass myObject = await MyClass.CreateAsync(...); // use the created object: return await myObject.OtherFunctionAsync(4, 7); } 

В этом конкретном случае, viewModel требуется для запуска задачи и уведомления о ее завершении. «Асинхронное свойство», а не «asynchronous конструктор», имеет порядок.

Я только что выпустил AsyncMVVM , который решает именно эту проблему (среди прочего). Если вы его используете, ваша ViewModel станет:

 public class ViewModel : AsyncBindableBase { public ObservableCollection Data { get { return Property.Get(GetDataAsync); } } private Task> GetDataAsync() { //Get the data asynchronously } } 

Как ни странно, Silverlight поддерживается. 🙂

если вы создадите asynchronous конструктор, после создания объекта вы можете столкнуться с такими проблемами, как пустые значения вместо объектов экземпляра. Например;

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

Вот почему они этого не догадываются.

вызов 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

Мне просто интересно, почему мы не можем позвонить, await от конструктора напрямую.

Я считаю, что короткий ответ просто: потому что команда .Net не запрограммировала эту функцию.

Я считаю, что с правильным синтаксисом это может быть реализовано и не должно быть слишком запутанным или подверженным ошибкам. Я думаю, что сообщение в блоге Стивена Клири и несколько других ответов здесь неявно отметили, что нет основополагающей причины для этого, и более того – решил, что недостает обходных решений. Существование этих относительно простых обходных решений, вероятно, является одной из причин, почему эта функция еще не реализована.

Я не знаком с ключевым словом async (это зависит от Silverlight или новой функции в бета-версии Visual Studio?), Но я думаю, что могу дать вам представление о том, почему вы не можете этого сделать.

Если я сделаю:

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

o не может быть инициализировано до следующей строки кода. Невозможно назначить экземпляр вашего объекта до тех пор, пока ваш конструктор не будет завершен, и создание асинхронного конструктора не изменится, так что будет точкой? Тем не менее, вы можете вызвать asynchronous метод из своего конструктора, а затем ваш конструктор может завершиться, и вы получите свой экземпляр, в то время как метод async все еще делает все, что ему нужно для настройки вашего объекта.

вы можете использовать Action in Constructor

  public class ViewModel { public ObservableCollection Data { get; set; } public ViewModel() { new Action(async () => { Data = await GetDataTask(); }).Invoke(); } public Task> GetDataTask() { Task> 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 load) { try { MyDataTable = await Task.Run(load); } catch (Exception ex) { //log } } private DataTable GetData() { DataTable data; // get data and return return data; } } 

Это как можно ближе к конструкторам.

  • Конструктор по умолчанию с пустыми скобками
  • Должен ли я создавать экземпляры переменных экземпляра в объявлении или в конструкторе?
  • Delphi: Понимание конструкторов
  • Когда правильно для конструктора выбрасывать исключение?
  • Как эта `this` ссылка на внешний class исчезает из-за публикации экземпляра внутреннего classа?
  • Может ли абстрактный class иметь конструктор?
  • Построение конструктора копирования в Java
  • Что делает двоеточие после имени конструктора C ++?
  • Вызов метода из конструктора
  • Почему нельзя синхронизировать конструкторы Java?
  • Делает много в конструкторах плохо?
  • Interesting Posts

    Как получить ссылку на делегат приложения в Swift?

    Evolution на Debian 7 не будет восстанавливать резервный файл Evolution от Fedora 18

    Разрешение экрана дисплея 8.6 VM не может быть увеличено

    Как стереть свободное место на диске в Linux?

    Ограничить длину текста до n строк с помощью CSS

    Как мне получить доступ к SmartCtl в Windows 7?

    Автоматическая блокировка экрана для любого пользователя после определенного периода бездействия в Windows 7

    delete vs delete в C ++

    android image exif reader Сторонняя api

    Android-студия logcat ничего не показывает

    Показывать экран всплеска во время загрузки основной формы

    Перейдите с модального представления на controller отображения панели вкладок и не потеряйте панель вкладок.

    Как автоматическое изменение размера и настройка элементов управления формы с изменением разрешения

    Gradle всегда выполняет печать из любой задачи

    jQuery: сделать флажки как радиокнопки?

    Давайте будем гением компьютера.