Chcę napisać metodę asynchroniczną z out
parametrem, takim jak ten:
public async void Method1()
{
int op;
int result = await GetDataTaskAsync(out op);
}
Jak to zrobić GetDataTaskAsync
?
źródło
Chcę napisać metodę asynchroniczną z out
parametrem, takim jak ten:
public async void Method1()
{
int op;
int result = await GetDataTaskAsync(out op);
}
Jak to zrobić GetDataTaskAsync
?
Nie możesz mieć metod asynchronicznych z parametrami ref
lub out
.
Lucian Wischik wyjaśnia, dlaczego nie jest to możliwe w tym wątku MSDN: http://social.msdn.microsoft.com/Forums/en-US/d2f48a52-e35a-4948-844d-828a1a6deb74/why-async-methods-cannot-have -ref-or-out-parameters
Co do tego, dlaczego metody asynchroniczne nie obsługują parametrów zewnętrznych? (lub parametry ref?) To jest ograniczenie CLR. Zdecydowaliśmy się zaimplementować metody asynchroniczne w podobny sposób jak metody iteracyjne - tj. Poprzez kompilator przekształcający metodę w obiekt maszyny stanu. Środowisko CLR nie ma bezpiecznego sposobu przechowywania adresu „parametru wyjściowego” lub „parametru odniesienia” jako pola obiektu. Jedynym sposobem na uzyskanie obsługiwanych parametrów poza odwołaniem byłoby wykonanie funkcji asynchronicznej przez przepisanie środowiska CLR niskiego poziomu zamiast przepisywania przez kompilator. Przeanalizowaliśmy to podejście i wymagało to wielu działań, ale ostatecznie byłoby to tak kosztowne, że nigdy by się nie wydarzyło.
Typowym obejściem tej sytuacji jest zwrócenie przez metodę asynchroniczną krotki. Możesz ponownie napisać swoją metodę jako taką:
public async Task Method1()
{
var tuple = await GetDataTaskAsync();
int op = tuple.Item1;
int result = tuple.Item2;
}
public async Task<Tuple<int, int>> GetDataTaskAsync()
{
//...
return new Tuple<int, int>(1, 2);
}
Tuple
alternatywę. Bardzo pomocne.Tuple
. : PNie możesz mieć parametrów
ref
lubout
wasync
metodach (jak już wspomniano).To krzyczy o pewne modelowanie w poruszających się danych:
Zyskujesz możliwość łatwiejszego ponownego użycia kodu, a ponadto jest on o wiele bardziej czytelny niż zmienne lub krotki.
źródło
Rozwiązanie C # 7 + polega na użyciu niejawnej składni krotki.
zwracany wynik wykorzystuje nazwy właściwości zdefiniowanych w sygnaturach metody. na przykład:
źródło
Alex poruszył wielką kwestię czytelności. Równoważnie funkcja jest również wystarczającym interfejsem, aby zdefiniować zwracane typy, a także uzyskać zrozumiałe nazwy zmiennych.
Wywołujące zapewniają lambdę (lub nazwaną funkcję), a funkcja Intellisense pomaga, kopiując nazwy zmiennych od delegata.
To szczególne podejście przypomina metodę „Try”, w której
myOp
jest ustawiana, jeśli wynik metody totrue
. W przeciwnym razie nie przejmujesz sięmyOp
.źródło
Jedną z fajnych cech
out
parametrów jest to, że mogą być używane do zwracania danych nawet wtedy, gdy funkcja zgłasza wyjątek. Myślę, że najbliższym odpowiednikiem zrobienia tego za pomocąasync
metody byłoby użycie nowego obiektu do przechowywania danych, do którychasync
może się odnosić zarówno metoda, jak i wywołujący. Innym sposobem byłoby przekazanie delegata zgodnie z sugestią zawartą w innej odpowiedzi .Zauważ, że żadna z tych technik nie będzie miała żadnego rodzaju wymuszenia ze strony kompilatora, który
out
ma. Oznacza to, że kompilator nie będzie wymagał ustawienia wartości udostępnionego obiektu ani wywołania przekazanego delegata.Oto przykładowa implementacja korzystająca z udostępnionego obiektu do naśladowania
ref
iout
do użytku zasync
metodami i innymi różnymi scenariuszami, w którychref
iout
nie są dostępne:źródło
Uwielbiam
Try
wzór. To uporządkowany wzór.Ale to trudne
async
. To nie znaczy, że nie mamy prawdziwych opcji. Oto trzy podstawowe podejścia, które można rozważyć w przypadkuasync
metod w quasi-wersjiTry
wzorca.Podejście 1 - wyprowadź konstrukcję
Wygląda to najbardziej jak
Try
metoda synchronizacji zwracająca tylko atuple
zamiast abool
zout
parametrem, który, jak wszyscy wiemy, jest niedozwolony w C #.Ze sposobu, który powraca
true
zfalse
i nigdy nie zgłaszaexception
.Podejście 2 - przekazanie metod wywołania zwrotnego
Możemy użyć
anonymous
metod do ustawienia zmiennych zewnętrznych. To sprytna składnia, choć nieco skomplikowana. W małych dawkach jest w porządku.Metoda jest zgodna z podstawami
Try
wzorca, ale ustawiaout
parametry przekazywane w metodach wywołania zwrotnego. Robi się to w ten sposób.Podejście 3 - użyj ContinueWith
A co, jeśli po prostu użyjesz zgodnego z
TPL
projektem? Żadnych krotek. Chodzi o to, że używamy wyjątków, aby przekierowywaćContinueWith
na dwie różne ścieżki.Z metodą, która rzuca,
exception
kiedy pojawia się jakikolwiek błąd. To coś innego niż zwrócenie plikuboolean
. To sposób na komunikację zTPL
.W powyższym kodzie, jeśli plik nie zostanie znaleziony, zostanie zgłoszony wyjątek. Spowoduje to wywołanie błędu,
ContinueWith
który będzie obsługiwanyTask.Exception
w jego bloku logicznym. Schludnie, co?Powodzenia.
źródło
ContinueWith
połączeń daje oczekiwany wynik? Zgodnie z moim zrozumieniem, drugaContinueWith
będzie sprawdzać powodzenie pierwszej kontynuacji, a nie powodzenie pierwotnego zadania.Miałem ten sam problem, co lubię, używając wzorca metody Try, który zasadniczo wydaje się niekompatybilny z paradygmatem async-await ...
Ważne dla mnie jest to, że mogę wywołać metodę Try w pojedynczej klauzuli if i nie muszę wcześniej definiować zmiennych wyjściowych, ale mogę to zrobić w linii, jak w poniższym przykładzie:
Więc wymyśliłem następujące rozwiązanie:
Zdefiniuj strukturę pomocniczą:
Zdefiniuj metodę async Try w następujący sposób:
Wywołaj metodę async Try w następujący sposób:
Dla wielu parametrów wyjściowych można zdefiniować dodatkowe struktury (np. AsyncOut <T, OUT1, OUT2>) lub zwrócić krotkę.
źródło
Ograniczenie
async
metod, które nie akceptująout
parametrów, dotyczy tylko metod asynchronicznych generowanych przez kompilator, które zostały zadeklarowane za pomocąasync
słowa kluczowego. Nie dotyczy to ręcznie tworzonych metod asynchronicznych. Innymi słowy, można tworzyćTask
metody zwracające akceptująceout
parametry. Na przykład powiedzmy, że mamy jużParseIntAsync
metodę, która rzuca i chcemy stworzyć takąTryParseIntAsync
, która nie rzuca. Moglibyśmy to zaimplementować w ten sposób:Używanie
TaskCompletionSource
i tenContinueWith
sposób jest nieco niewygodne, ale nie ma innej opcji, ponieważ nie mogą korzystać z wygodnegoawait
słowa kluczowego wewnątrz tej metody.Przykład użycia:
Aktualizacja: jeśli logika asynchroniczna jest zbyt złożona, aby można ją było wyrazić bez
await
, można ją hermetyzować wewnątrz zagnieżdżonego asynchronicznego anonimowego delegata.TaskCompletionSource
Nadal będzie potrzebna dlaout
parametru. Możliwe, żeout
parametr mógłby zostać uzupełniony przed zakończeniem głównego zadania, jak w poniższym przykładzie:W tym przykładzie zakłada istnienie trzech metod asynchronicznych
GetResponseAsync
,GetRawDataAsync
iFilterDataAsync
które są nazywane w drugim.out
Parametru wykonany w zakończeniu drugiego sposobu.GetDataAsync
Metoda może być stosowana tak:Oczekiwanie na
data
przed oczekiwaniem na znakrawDataLength
jest ważne w tym uproszczonym przykładzie, ponieważ w przypadku wyjątkuout
parametr nigdy nie zostanie uzupełniony.źródło
Myślę, że takie użycie ValueTuples może działać. Musisz jednak najpierw dodać pakiet ValueTuple NuGet:
źródło
Oto kod odpowiedzi @ dcastro zmodyfikowany dla C # 7.0 z nazwanymi krotkami i dekonstrukcją krotek, co usprawnia notację:
Aby uzyskać szczegółowe informacje na temat nowych nazwanych krotek, literałów krotek i dekonstrukcji krotek, zobacz: https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/
źródło
Możesz to zrobić za pomocą TPL (biblioteki równoległej zadań) zamiast bezpośrednio za pomocą słowa kluczowego await.
źródło