Jaki byłby najbardziej elegancki sposób wywołania metody asynchronicznej z modułu pobierającego lub ustawiającego w języku C #?
Oto pseudo-kod, który pomoże mi się wyjaśnić.
async Task<IEnumerable> MyAsyncMethod()
{
return await DoSomethingAsync();
}
public IEnumerable MyList
{
get
{
//call MyAsyncMethod() here
}
}
Task<T>
, która natychmiast zwróci, będzie miała normalną semantykę właściwości i nadal pozwoli na traktowanie rzeczy asynchronicznie w razie potrzeby.Odpowiedzi:
Nie ma technicznego powodu, aby
async
właściwości nie były dozwolone w języku C #. Była to celowa decyzja projektowa, ponieważ „właściwości asynchroniczne” to oksymoron.Właściwości powinny zwracać bieżące wartości; nie powinny rozpoczynać operacji w tle.
Zwykle, gdy ktoś chce „właściwości asynchronicznej”, tak naprawdę chce jednej z tych rzeczy:
async
metodę.async
metody fabrycznej dla obiektu zawierającego lub użyjasync InitAsync()
metody. Wartość powiązana z danymi będzie obowiązywaćdefault(T)
do momentu obliczenia / pobrania wartości.AsyncLazy
z mojego bloga lub biblioteki AsyncEx . To da ciawait
właściwość.Aktualizacja: I obejmować właściwości asynchronicznych w jednym z moich ostatnich „asynchronicznych OOP” blogach.
źródło
Dispatcher.CurrentDispatcher.Invoke(new Action(..)
?INotifyPropertyChanged
, a następnie zdecyduj, czy chcesz zwrócić starą wartość, czydefault(T)
też trwa aktualizacja asynchroniczna.NotifyTaskCompletion
z mojego projektu AsyncEx . Lub możesz zbudować własny; to nie jest takie trudne.{Binding PropName.Result}
nie jest dla mnie trywialne.Nie można go nazwać asynchronicznie, ponieważ nie ma asynchronicznej obsługi właściwości, tylko metody asynchroniczne. Jako takie istnieją dwie opcje, obie wykorzystujące fakt, że metody asynchroniczne w CTP są tak naprawdę tylko metodą zwracającą
Task<T>
lubTask
:Lub:
źródło
private async void SetupList() { MyList = await MyAsyncMethod(); }
tej. Spowodowałoby to ustawienie MyList (a następnie automatyczne powiązanie, jeśli implementuje INPC), jak tylko operacja asynchroniczna zakończy się ...getTitle()
...Naprawdę potrzebowałem wywołania, aby pochodziło z metody get, ze względu na moją oddzieloną architekturę. Więc wymyśliłem następującą implementację.
Użycie: Tytuł znajduje się w ViewModel lub obiekcie, który można statycznie zadeklarować jako zasób strony. Powiąż z nim, a wartość zostanie zapełniona bez blokowania interfejsu użytkownika, gdy funkcja getTitle () zwróci.
źródło
Myślę, że możemy poczekać na wartość zwracającą najpierw zero, a następnie uzyskaną prawdziwą wartość, więc w przypadku Pure MVVM (na przykład projekt PCL) uważam, że najbardziej eleganckim rozwiązaniem jest:
źródło
CS4014: Async method invocation without an await expression
async void
tylko dla osób zajmujących się obsługą najwyższego poziomu i podobnych”. Myślę, że można to zakwalifikować jako „i im podobne”.Możesz użyć w
Task
ten sposób:źródło
Myślałem, że .GetAwaiter (). GetResult () było właśnie rozwiązaniem tego problemu, nie? na przykład:
źródło
.Result
- nie jest asynchroniczne i może doprowadzić do impasu.Ponieważ Twoja „właściwość asynchroniczna” znajduje się w modelu widoku, możesz użyć AsyncMVVM :
Zadba o kontekst synchronizacji i powiadomienie o zmianie właściwości.
źródło
Nekromancja.
W .NET Core / NetStandard2 możesz użyć
Nito.AsyncEx.AsyncContext.Run
zamiastSystem.Windows.Threading.Dispatcher.InvokeAsync
:Jeśli po prostu wybierzesz
System.Threading.Tasks.Task.Run
lubSystem.Threading.Tasks.Task<int>.Run
, to nie zadziała.źródło
Myślę, że mój przykład poniżej może być zgodny z podejściem Stephena-Cleary'ego, ale chciałem podać zakodowany przykład. Jest to używane w kontekście powiązania danych, na przykład Xamarin.
Konstruktor klasy - a nawet ustawiający inną właściwość, od której jest zależny - może wywołać pustkę asynchroniczną, która zapełni właściwość po zakończeniu zadania bez potrzeby oczekiwania lub bloku. Kiedy w końcu uzyska wartość, zaktualizuje interfejs użytkownika za pośrednictwem mechanizmu NotifyPropertyChanged.
Nie jestem pewien co do jakichkolwiek skutków ubocznych wywołania aysnc void od konstruktora. Być może komentator opracuje obsługę błędów itp.
źródło
Kiedy natknąłem się na ten problem, próba uruchomienia synchronizacji metody asynchronicznej z narzędzia ustawiającego lub konstruktora doprowadziła mnie do impasu w wątku interfejsu użytkownika, a użycie procedury obsługi zdarzeń wymagało zbyt wielu zmian w ogólnym projekcie.
Rozwiązaniem było, jak to często bywa, po prostu jawne napisanie, co chciałem, niejawnie, co miało mieć inny wątek obsługujący operację i sprawienie, aby główny wątek czekał na zakończenie:
Można argumentować, że nadużywam frameworka, ale działa.
źródło
Sprawdzam wszystkie odpowiedzi, ale wszystkie mają problem z wydajnością.
na przykład w:
Deployment.Current.Dispatcher.InvokeAsync (async () => {Title = czekaj na getTitle ();});
użyj dyspozytora, co nie jest dobrą odpowiedzią.
ale istnieje proste rozwiązanie, po prostu zrób to:
źródło
Możesz zmienić właściwość na
Task<IEnumerable>
i zrób coś takiego:
i używaj go tak, jak czekasz na MyList;
źródło