Che ne dici di non avere una parola chiave?
Vorrei che il compilatore rendesse conto che, il più delle volte, quando chiamo un metodo asincrono, voglio il risultato.
Document doc = DownloadDocumentAsync();
Questo è tutto. Il motivo per cui le persone hanno difficoltà a pensare a una parola chiave per questa cosa è perché è come avere una parola chiave per "fai la cosa che faresti se le cose fossero perfettamente normali". Questo dovrebbe essere il valore predefinito, non richiedere una parola chiave.
Aggiorna
Inizialmente avevo suggerito che il compilatore dovesse diventare intelligente con l'inferenza di tipo per capire cosa fare. Pensandoci meglio, manterrò l'implementazione esistente nel CTP così com'è, ma apporterò un paio di aggiunte banali, in modo da ridurre i casi in cui è necessario utilizzare esplicitamente la parola chiave await
.
Inventiamo un attributo: [AutoAwait]
. Questo può essere applicato solo ai metodi. Un modo per farlo applicare al tuo metodo è contrassegnarlo async
. Ma potresti anche farlo a mano, ad esempio:
[AutoAwait]
public Task<Document> DownloadDocumentAsync()
Quindi, all'interno di qualsiasi metodo async
, il compilatore supporrà che desideri attendere su una chiamata a DownloadDocumentAsync
, quindi non devi specificarlo. Qualsiasi chiamata a tale metodo la attenderà automaticamente.
Document doc = DownloadDocumentAsync();
Ora, se vuoi "diventare furbo" e ottenere Task<Document>
, usi un operatore start
, che può comparire solo prima di una chiamata al metodo:
Task<Document> task = start DownloadDocumentAsync();
Neat, penso. Ora una semplice chiamata di metodo significa cosa significa in genere: attendere il completamento del metodo. E start
indica qualcosa di diverso: non aspettare.
Per il codice visualizzato al di fuori di un metodo async
, l'unico modo in cui ti è consentito chiamare un metodo [AutoAwait]
è precedendolo con start
. Questo ti costringe a scrivere codice che ha lo stesso significato indipendentemente dal fatto che appaia in un metodo async
o meno.
Allora inizio ad essere avido! :)
In primo luogo, voglio async
da applicare ai metodi di interfaccia:
interface IThing
{
async int GetCount();
}
In pratica significa che il metodo di implementazione deve restituire Task<int>
o qualcosa compatibile con await
, e chi chiama al metodo otterrà il comportamento di [AutoAwait]
.
Anche quando implemento il metodo sopra, voglio poter scrivere:
async int GetCount()
Quindi non devo menzionare Task<int>
come tipo di ritorno.
Inoltre, voglio async
da applicare a tipi di delegati (che, dopotutto, sono come interfacce con un metodo). Quindi:
public async delegate TResult AsyncFunc<TResult>();
Un async
delegato ha - hai indovinato - [AutoAwait]
comportamento. Da un metodo async
puoi chiamarlo e sarà automaticamente await
ed (a meno che tu non scelga solo start
). E quindi se dici:
AsyncFunc<Document> getDoc = DownloadDocumentAsync;
Questo funziona. Non è una chiamata al metodo. Nessuna attività è stata ancora avviata: una async delegate
non è un'attività. È una fabbrica per fare compiti. Puoi dire:
Document doc = getDoc();
E ciò avvierà un'attività e attenderà che finisca e ti dia il risultato. Oppure puoi dire:
Task<Document> t = start getDoc();
Quindi, un punto in cui è presente "l'impianto idraulico" è che se si desidera eseguire un delegato su un metodo async
, è necessario conoscere un tipo async delegate
. Quindi, invece di Func
devi dire AsyncFunc
, e così via. Anche se un giorno quel genere di cose potrebbe essere corretto migliorando l'inferenza del tipo.
Un'altra domanda è cosa dovrebbe succedere se dici di iniziare con un metodo ordinario (non asincrono). Ovviamente un errore di compilazione sarebbe l'opzione sicura. Ma ci sono altre possibilità.