Я пытаюсь следовать шаблону RAII в моих классах обслуживания, а это означает, что при создании объекта он полностью инициализируется. Однако я сталкиваюсь с трудностями с асинхронными API. Структура рассматриваемого класса выглядит следующим образом
class ServiceProvider : IServiceProvider // Is only used through this interface
{
public int ImportantValue { get; set; }
public event EventHandler ImportantValueUpdated;
public ServiceProvider(IDependency1 dep1, IDependency2 dep2)
{
// IDependency1 provide an input value to calculate ImportantValue
// IDependency2 provide an async algorithm to calculate ImportantValue
}
}
Я также нацелен на устранение побочных эффектов в ImportantValue
getter, чтобы сделать его потокобезопасным.
Теперь пользователи ServiceProvider
создадут его экземпляр, подпишитесь на событие изменения ImportantValue
и получите начальный ImportantValue
. И здесь возникает проблема с начальным значением. Поскольку ImportantValue
вычисляется асинхронно, класс не может быть полностью инициализирован в конструкторе. Может быть, правильно иметь это значение как нуль изначально, но тогда мне нужно иметь место, где оно будет вычисляться в первый раз. Естественным местом для этого может быть геттер ImportantValue
, но я нацелен на то, чтобы сделать его потокобезопасным и без побочных эффектов.
Поэтому я в основном придерживаюсь этих противоречий. Не могли бы вы помочь мне и предложить альтернативу? Наличие значения, инициализированного в конструкторе, в то время как славное на самом деле не является необходимым, но никаких побочных эффектов и безопасности потоков не является обязательным.
Спасибо заранее.
EDIT: Еще одна вещь, которую нужно добавить. Я использую Ninject для создания экземпляра, и, насколько я понимаю, он не поддерживает методы async для создания привязки. Хотя подход с инициированием некоторых операций на основе задач в конструкторе будет работать, я не могу дождаться его результата.
т.е. два следующих подхода (предлагаемые в качестве ответов до сих пор) не будут компилироваться, поскольку возвращается Задача, а не мой объект:
Kernel.Bind<IServiceProvider>().ToMethod(async ctx => await ServiceProvider.CreateAsync())
или
Kernel.Bind<IServiceProvider>().ToMethod(async ctx =>
{
var sp = new ServiceProvider();
await sp.InitializeAsync();
})
Простое связывание будет работать, но я не ожидаю результата асинхронной инициализации, запущенного в конструкторе, как было предложено Стивеном Клири:
Kernel.Bind<IServiceProvider>().To<ServiceProvider>();
... и это не выглядит хорошо для меня.