TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.76K subscribers
Post #1647 1.33K
День 1328. #ЗаметкиНаПолях #AsyncTips
Асинхронное Создание Объектов: Фабрики

Задача:
вы создаёте тип, который требует выполнения некоторой асинхронной работы в конструкторе.

Решение
Конструкторы не могут объявляться с async; кроме того, они не могут содержать ключевое слово await. Конечно, использование await в конструкторе могло бы быть полезным, но это привело бы к существенному изменению языка C#.

Одна из возможностей — использовать конструктор в паре с инициализирующим async-методом, чтобы тип использовался следующим образом:
var instance = new MyAsyncClass();
await instance.InitAsync();

У такого подхода есть недостатки. Разработчик может забыть вызвать метод InitAsync, а экземпляр не может использоваться сразу же после выполнения конструктора.

Вот более качественное решение, которое основано на применении паттерна асинхронного фабричного метода:
class AsyncClass
{
private AsyncClass()
{
}

private async Task<AsyncClass> InitAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1));
return this;
}

public static Task<AsyncClass> CreateAsync()
{
var result = new AsyncClass();
return result.InitAsync();
}
}

Конструктор и метод InitAsync объявлены приватными, чтобы они не могли использоваться в вызывающем коде; экземпляры могут создаваться только одним способом — статическим фабричным методом CreateAsync. Вызывающий код не может обратиться к экземпляру до того, как инициализация будет завершена.
Экземпляр может создаваться следующим образом:
var instance = await AsyncClass.CreateAsync();

Главное преимущество этого паттерна заключается в том, что вызывающий код никак не сможет получить неинициализированный экземпляр AsyncClass.

К сожалению, в некоторых сценариях этот способ не работает — в частности, когда в коде используется провайдер внедрения зависимостей. Ни одна заметная библиотека внедрения зависимостей или инверсии управления не работает с async-кодом. Если вы окажетесь в одной из таких ситуаций, существует пара альтернатив, которые также стоит рассмотреть.

Если создаваемый экземпляр в действительности является общим ресурсом, можно использовать ленивую инициализацию. В противном случае можно воспользоваться паттерном асинхронной инициализации, который рассмотрим позже.

Пример того, как поступать не следует:
class AsyncClass
{
public AsyncClass()
{
InitAsync();
}
// ПЛОХОЙ КОД!!
private async void InitAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1));
}
}

На первый взгляд решение может показаться разумным: вы получаете обычный конструктор, который запускает асинхронную операцию; при этом у него есть ряд недостатков, обусловленных использованием async void. Первая проблема заключается в том, что при завершении конструктора экземпляр всё ещё продолжает асинхронно инициализироваться, и не существует очевидного способа определить, когда завершится асинхронная инициализация. Вторая проблема связана с обработкой ошибок: любые исключения, выданные из InitAsync, не могут быть перехвачены секциями catch, окружающими вызов конструктора объекта.

Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 11.
  • 👍 12
More from @netdeveloperdiary
  1. Oct 13, 2026День 2813. #ЗаметкиНаПолях #Async Ещё Раз Про Async/Await в C#. Часть 1 1. Почему существу…
  2. Oct 12, 2026День 2812. #Карьера #AI Как Разработчикам Оставаться Полезными, Когда Код Пишет ИИ? По мер…
  3. Oct 11, 2026День 2811. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  4. Oct 10, 2026День 2810. #ЧтоНовенького #VSCode Более Быстрый и Лёгкий C# Dev Kit Мы, разработчики, люби…
  5. Oct 9, 2026День 2809. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Окончание Начало 3.…
  6. Oct 8, 2026День 2808. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Начало В ИТ есть се…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →