День 1345. #ЗаметкиНаПолях #AsyncTips
Асинхронное Создание Объектов: Паттерн Асинхронной Инициализации
Задача
Вам нужен тип, требующий выполнения некоторой асинхронной работы в конструкторе, но вы не можете воспользоваться паттерном асинхронной фабрики, так как экземпляр создаётся с применением рефлексии (например, IoC-контейнера, связывания данных, Activator.CreateInstance и т. д.).
Решение
В таком сценарии вам приходится возвращать неинициализированный экземпляр, хотя ситуацию можно частично сгладить применением распространённого паттерна асинхронной инициализации. Каждый тип, требующий асинхронной инициализации, должен определять специальное свойство (обычно в интерфейсе-маркере):
public interface IAsyncInit
{
Task Initialization { get; }
}
При реализации этого паттерна следует начать инициализацию, задав значение свойства Initialization в конструкторе. Доступ к результатам асинхронной инициализации (вместе с любыми исключениями) предоставляется через свойство Initialization. Пример реализации:
class MyType : IAsyncInit
{
public MyType()
{
Initialization = InitAsync();
}
public Task Initialization
{ get; private set; }
private async Task InitAsync()
{
// асинхронно инициализируем экземпляр
await Task.Delay(TimeSpan.FromSeconds(1));
}
}
Экземпляр этого типа может быть создан и инициализирован примерно так:
var instance = DIContainer.Resolve<MyType>();
var asyncInit = instance as IAsyncInit;
if (asyncInit != null)
await asyncInit.Initialization;
По возможности рекомендуется применять асинхронные фабрики или ленивую инициализацию вместо этого решения. Эти решения предпочтительны, потому что в них исключается доступ к неинициализированному экземпляру. Если ваши экземпляры создаются библиотеками внедрения зависимостей/инверсии управления, связывания данных и т. д., и вы вынуждены открыть доступ к неинициализированному экземпляру, придётся использовать паттерн асинхронной инициализации.
Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 11.