Как насчет отсутствия ключевого слова?
Я бы хотел, чтобы компилятор понял, что большую часть времени, когда я вызываю асинхронный метод, я хочу получить его результат.
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и так далее. Хотя однажды такие вещи можно исправить с помощью улучшенного вывода типа.
Другой вопрос, что должно произойти, если вы скажете начать с обычного (не асинхронного) метода. Очевидно, что ошибка компиляции будет безопасным вариантом. Но есть и другие возможности.