Як щодо того, щоб не було ключового слова?
Я хотів би, щоб компілятор усвідомлював, що більшість часу, коли я викликаю асинхронний метод, я хочу, щоб результат його був.
Document doc = DownloadDocumentAsync();
Це воно. Причина, чому люди важко придумують ключове слово для цієї речі, це тому, що це як ключове слово для "робити те, що ти робив би, якби справи були абсолютно нормальними". Це повинно бути за замовчуванням, не вимагати ключового слова.
Оновлення
Спочатку я запропонував компілятору отримати розумний висновок про тип, щоб зрозуміти, що робити. Думаючи далі про це, я б зберег існуючу реалізацію в CTP такою, якою вона є, але внести до неї кілька дрібницьких доповнень, щоб зменшити випадки, коли вам потрібно буде await
чітко використовувати ключове слово.
Ми винаходимо атрибут: [AutoAwait]
. Це можна застосувати лише до методів. Один із способів застосувати це до вашого методу - позначити його async
. Але ви також можете це зробити вручну, наприклад:
[AutoAwait]
public Task<Document> DownloadDocumentAsync()
Тоді всередині будь-якого async
методу компілятор припустить, що ви хочете чекати на виклик DownloadDocumentAsync
, тому не потрібно його вказувати. Будь-який дзвінок до цього методу автоматично його чекає.
Document doc = DownloadDocumentAsync();
Тепер, якщо ви хочете "стати розумним" і отримати його Task<Document>
, ви використовуєте оператор start
, який може з'явитися лише перед викликом методу:
Task<Document> task = start DownloadDocumentAsync();
Охайний, я думаю. Тепер звичайний виклик методу означає те, що він зазвичай означає: чекайте завершення методу. І start
вказує на щось інше: не чекай.
Для коду, який з’являється поза async
методом, єдиний спосіб, яким ви можете викликати [AutoAwait]
метод, - це префіксація start
. Це змушує вас писати код, який має однакове значення незалежно від того, відображається він у async
методі чи ні.
Тоді я починаю жадібний! :)
По-перше, я хочу async
застосувати методи інтерфейсу:
interface IThing
{
async int GetCount();
}
Це в основному означає, що метод реалізації повинен повернутися Task<int>
або щось сумісне з await
, і абоненти до методу отримають [AutoAwait]
поведінку.
Також коли я реалізую вищевказаний метод, я хочу мати можливість писати:
async int GetCount()
Тому я не повинен згадувати Task<int>
як тип повернення.
Також я хочу async
звернутися до типів делегатів (які, зрештою, схожі на інтерфейси одного методу). Так:
public async delegate TResult AsyncFunc<TResult>();
async
Делегат має - ви вже здогадалися - [AutoAwait]
поведінка. З async
методу ви можете викликати його, і він буде автоматично await
відредагований (якщо ви не вирішите його просто start
). Так що, якщо ви кажете:
AsyncFunc<Document> getDoc = DownloadDocumentAsync;
Це просто працює. Це не виклик методу. Жодне завдання ще не розпочато - async delegate
це не завдання. Це фабрика для виконання завдань. Ви можете сказати:
Document doc = getDoc();
І це почне завдання і чекатиме його закінчення і дасть результат. Або ви можете сказати:
Task<Document> t = start getDoc();
Тож одне місце в цьому місці, де "сантехніка" протікає, це те, що якщо ви хочете зробити делегат async
методу, ви повинні знати async delegate
тип. Тож замість Func
вас треба сказати AsyncFunc
тощо. Хоча одного дня такі речі можуть бути виправлені за допомогою поліпшеного виводу типу.
Інше питання - що має статися, якщо ви скажете почати із звичайного (несинхронного) методу. Очевидно, що помилка компіляції була б безпечним варіантом. Але є й інші можливості.