Jak zainicjować obiekt za pomocą wzorca asynchronicznego oczekiwania
Próbuję podążać za wzorcem RAII w moich klasach usług, co oznacza, że gdy obiekt jest konstruowany, jest w pełni inicjowany. Mam jednak problemy z asynchronicznymi interfejsami API. Struktura omawianej klasy wygląda następująco
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
}
}
Mam również na celu pozbycie się efektów ubocznychImportantValue
getter, aby był bezpieczny dla wątków.
Teraz użytkownicyServiceProvider
utworzy jego wystąpienie, zasubskrybuje wydarzenieImportantValue
zmienić i uzyskać początkowyImportantValue
. I tu pojawia się problem z wartością początkową. OdImportantValue
jest obliczany asynchronicznie, klasy nie można w pełni zainicjować w konstruktorze. Początkowo może mieć wartość null, ale muszę mieć miejsce, w którym będzie obliczane po raz pierwszy. Naturalnym miejscem na to może byćImportantValue
getter, ale zamierzam uczynić go bezpiecznym dla wątków i bez skutków ubocznych.
Tak więc zasadniczo utknąłem w tych sprzecznościach. Czy mógłbyś mi pomóc i zaproponować alternatywę? Posiadanie wartości zainicjowanej w konstruktorze, gdy ładne, nie jest naprawdę konieczne, ale nie są wymagane żadne efekty uboczne i bezpieczeństwo wątku.
Z góry dziękuję.
EDYTOWAĆ: Jeszcze jedna rzecz do dodania. Używam Ninject do tworzenia instancji i, o ile rozumiem, nie obsługuje asynchronicznych metod tworzenia powiązań. Chociaż podejście do inicjowania niektórych operacji opartych na zadaniach w konstruktorze będzie działać, nie mogę czekać na jego wynik.
To znaczy. dwa kolejne podejścia (dotychczas dostępne jako odpowiedzi) nie będą się kompilować, ponieważ zadanie jest zwracane, a nie mój obiekt:
Kernel.Bind<IServiceProvider>().ToMethod(async ctx => await ServiceProvider.CreateAsync())
lub
Kernel.Bind<IServiceProvider>().ToMethod(async ctx =>
{
var sp = new ServiceProvider();
await sp.InitializeAsync();
})
Proste wiązanie będzie działać, ale nie oczekuję wyniku asynchronicznej inicjalizacji rozpoczętej w konstruktorze, jak zaproponował Stephen Cleary:
Kernel.Bind<IServiceProvider>().To<ServiceProvider>();
... a to nie wygląda dobrze dla mnie.