Co powiesz na brak słowa kluczowego?
Chciałbym, aby kompilator zdał sobie sprawę, że przez większość czasu, gdy wywołuję metodę asynchroniczną, chcę jej wynik.
Document doc = DownloadDocumentAsync();
Otóż to. Powodem, dla którego ludzie mają trudności z wymyśleniem słowa kluczowego dla tej rzeczy, jest to, że jest to słowo kluczowe dla „rób to, co zrobiłbyś, gdyby wszystko było całkowicie normalne”. To powinno być domyślne, nie wymagać słowa kluczowego.
Aktualizacja
Początkowo zasugerowałem, że kompilator powinien być sprytny z wnioskami o typ, aby dowiedzieć się, co robić. Zastanawiając się nad tym, chciałbym zachować obecną implementację w CTP w obecnej postaci, ale dodam do niej kilka trywialnych dodatków, aby zmniejszyć liczbę przypadków, w których konieczne byłoby await
jawne użycie słowa kluczowego.
Wymyślamy atrybut: [AutoAwait]
. Można to zastosować tylko do metod. Jednym ze sposobów, aby zastosować to do metody, jest jej oznaczenie async
. Ale możesz to również zrobić ręcznie, np .:
[AutoAwait]
public Task<Document> DownloadDocumentAsync()
Następnie w każdej async
metodzie kompilator zakłada, że chcesz poczekać na wywołanie DownloadDocumentAsync
, więc nie musisz go określać. Każde wywołanie tej metody będzie automatycznie na nią czekało.
Document doc = DownloadDocumentAsync();
Teraz, jeśli chcesz „uzyskać spryt” i uzyskać Task<Document>
, użyj operatora start
, który może pojawić się tylko przed wywołaniem metody:
Task<Document> task = start DownloadDocumentAsync();
Zgrabnie, tak myślę. Teraz zwykłe wywołanie metody oznacza to, co zwykle oznacza: poczekaj na zakończenie metody. I start
wskazuje coś innego: nie czekaj.
W przypadku kodu, który pojawia się poza async
metodą, jedynym sposobem na wywołanie [AutoAwait]
metody jest przedrostek start
. Zmusza to do pisania kodu o tym samym znaczeniu, niezależnie od tego, czy pojawia się w async
metodzie, czy nie.
Potem zaczynam być chciwy! :)
Po pierwsze, chcę async
zastosować się do metod interfejsu:
interface IThing
{
async int GetCount();
}
Zasadniczo oznacza to, że metoda implementująca musi zwrócić Task<int>
lub coś kompatybilnego await
, a osoby wywołujące metodę otrzymają [AutoAwait]
zachowanie.
Również po wdrożeniu powyższej metody chcę móc pisać:
async int GetCount()
Więc nie muszę wspominać Task<int>
o typie zwrotu.
Chcę async
również zastosować do typów delegowanych (które przecież są jak interfejsy z jedną metodą). Więc:
public async delegate TResult AsyncFunc<TResult>();
async
Delegat posiada - zgadliście - [AutoAwait]
zachowanie. Z async
metody możesz ją wywołać, a zostanie ona automatycznie await
edytowana (chyba że zdecydujesz się to start
zrobić). A więc jeśli powiesz:
AsyncFunc<Document> getDoc = DownloadDocumentAsync;
To po prostu działa. To nie jest wywołanie metody. Żadne zadanie nie zostało jeszcze uruchomione - async delegate
nie jest zadaniem. To fabryka do wykonywania zadań. Możesz powiedzieć:
Document doc = getDoc();
A to rozpocznie zadanie i zaczekaj, aż się zakończy i da ci wynik. Lub możesz powiedzieć:
Task<Document> t = start getDoc();
Tak więc jednym z miejsc, w których przecieka „hydraulika”, jest to, że jeśli chcesz delegować async
metodę, musisz wiedzieć, że używasz async delegate
typu. Zamiast tego Func
musisz powiedzieć AsyncFunc
i tak dalej. Chociaż pewnego dnia tego rodzaju rzeczy mogą zostać naprawione przez lepsze wnioskowanie o typie.
Innym pytaniem jest, co powinno się stać, jeśli powiesz, że zacznij od zwykłej metody (nie asynchronicznej). Oczywiście błąd kompilacji byłby bezpieczną opcją. Ale są też inne możliwości.