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