Che ne dici di non avere una parola chiave?
Vorrei che il compilatore si 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". Dovrebbe essere l'impostazione predefinita, non richiedere una parola chiave.
Aggiornare
Inizialmente ho suggerito che il compilatore dovrebbe diventare intelligente con l'inferenza del tipo per capire cosa fare. Ripensandoci, manterrei l'implementazione esistente nel CTP così com'è, ma farei un paio di banali aggiunte ad esso, in modo da ridurre i casi in cui dovresti usare await
esplicitamente la parola chiave.
Inventiamo un attributo: [AutoAwait]
. Questo può essere applicato solo ai metodi. Un modo per applicare questo al tuo metodo è contrassegnarlo async
. Ma potresti anche farlo a mano, ad esempio:
[AutoAwait]
public Task<Document> DownloadDocumentAsync()
Quindi, all'interno di qualsiasi async
metodo, il compilatore supporrà di voler attendere una chiamata DownloadDocumentAsync
, quindi non è necessario specificarlo. Qualsiasi chiamata a quel metodo lo attenderà automaticamente.
Document doc = DownloadDocumentAsync();
Ora, se vuoi "diventare intelligente" e ottenere il Task<Document>
, usi un operatore start
, che può apparire solo prima di una chiamata di metodo:
Task<Document> task = start DownloadDocumentAsync();
Pulito, penso. Ora una semplice chiamata di metodo significa ciò che di solito significa: attendere il completamento del metodo. E start
indica qualcosa di diverso: non aspettare.
Per il codice che appare al di fuori di un async
metodo, l'unico modo in cui ti è consentito chiamare un [AutoAwait]
metodo è prefissarlo con start
. Questo ti costringe a scrivere codice che ha lo stesso significato indipendentemente dal fatto che appaia async
o meno in un metodo.
Quindi inizio a diventare avido! :)
In primo luogo, voglio async
applicare ai metodi di interfaccia:
interface IThing
{
async int GetCount();
}
Significa sostanzialmente che il metodo di implementazione deve tornare Task<int>
o qualcosa di compatibile con await
, e i chiamanti al metodo avranno [AutoAwait]
comportamento.
Anche quando implemento il metodo sopra, voglio essere in grado di scrivere:
async int GetCount()
Quindi non devo menzionare Task<int>
come tipo di ritorno.
Inoltre, voglio async
fare domanda per i tipi delegati (che, dopo tutto, sono come interfacce con un metodo). Così:
public async delegate TResult AsyncFunc<TResult>();
Un async
delegato ha - hai indovinato - un [AutoAwait]
comportamento. Da un async
metodo puoi chiamarlo e verrà automaticamente await
ed (a meno che tu non scelga di start
farlo). E così se dici:
AsyncFunc<Document> getDoc = DownloadDocumentAsync;
Funziona e basta. Non è una chiamata di metodo. Nessuna attività è stata ancora avviata: async delegate
non è un'attività. È una fabbrica per svolgere compiti. Si può dire:
Document doc = getDoc();
E questo avvierà un'attività e aspetterà che finisca e ti dia il risultato. Oppure puoi dire:
Task<Document> t = start getDoc();
Quindi un posto in questo in cui fuoriesce "l'idraulico" è che se vuoi fare un delegato a un async
metodo, devi sapere di usare un async delegate
tipo. Quindi, invece di Func
te, devi dire AsyncFunc
, e così via. Anche se un giorno quel genere di cose potrebbe essere risolto da una migliore inferenza di tipo.
Un'altra domanda è cosa dovrebbe accadere se dici di iniziare con un metodo ordinario (non asincrono). Ovviamente un errore di compilazione sarebbe l'opzione sicura. Ma ci sono altre possibilità.