¿Qué tal no tener una palabra clave?
Me gustaría que el compilador se dé cuenta de que, la mayoría de las veces, cuando llamo a un método asincrónico, quiero el resultado.
Document doc = DownloadDocumentAsync();
Eso es. La razón por la que las personas están teniendo dificultades para pensar una palabra clave para esto es porque es como tener una palabra clave para "hacer lo que haría si las cosas fueran perfectamente normales". Esa debería ser la predeterminada, no requiere una palabra clave.
Actualizar
Originalmente sugerí que el compilador debería ser inteligente con la inferencia de tipos para descubrir qué hacer. Pensando más en esto, mantendría la implementación existente en el CTP tal como es, pero haré un par de adiciones triviales, para reducir los casos en los que necesitaría usar la awaitpalabra clave explícitamente.
Nos inventamos un atributo: [AutoAwait]. Esto solo se puede aplicar a los métodos. Una forma de aplicar esto a su método es marcarlo async. Pero también puede hacerlo a mano, por ejemplo:
[AutoAwait]
public Task<Document> DownloadDocumentAsync()
Luego, dentro de cualquier asyncmétodo, el compilador asumirá que desea esperar una llamada DownloadDocumentAsync, por lo que no necesita especificarlo. Cualquier llamada a ese método lo esperará automáticamente.
Document doc = DownloadDocumentAsync();
Ahora, si desea "ser inteligente" y obtener el Task<Document>, utilice un operador start, que solo puede aparecer antes de una llamada al método:
Task<Document> task = start DownloadDocumentAsync();
Aseado, creo. Ahora, una llamada de método simple significa lo que generalmente significa: esperar a que se complete el método. E startindica algo diferente: no esperes.
Para el código que aparece fuera de un asyncmétodo, la única forma en que puede llamar a un [AutoAwait]método es mediante el prefijo start. Esto lo obliga a escribir código que tenga el mismo significado independientemente de si aparece en un asyncmétodo o no.
¡Entonces empiezo a ponerme codicioso! :)
En primer lugar, quiero asyncaplicar a los métodos de interfaz:
interface IThing
{
async int GetCount();
}
Básicamente significa que el método de implementación debe regresar Task<int>o algo compatible await, y las personas que llaman al método obtendrán un [AutoAwait]comportamiento.
Además, cuando implemente el método anterior, quiero poder escribir:
async int GetCount()
Así que no tengo que mencionar Task<int>como tipo de retorno.
Además, quiero asyncaplicar a los tipos de delegados (que, después de todo, son como interfaces con un método). Entonces:
public async delegate TResult AsyncFunc<TResult>();
Un asyncdelegado tiene, lo has adivinado, [AutoAwait]comportamiento. Desde un asyncmétodo puede llamarlo y se editará automáticamente await(a menos que elija simplemente start). Y entonces si dices:
AsyncFunc<Document> getDoc = DownloadDocumentAsync;
Eso solo funciona. No es una llamada al método. Aún no se ha iniciado ninguna tarea, y async delegateno es una tarea. Es una fábrica para hacer tareas. Puedes decir:
Document doc = getDoc();
Y eso comenzará una tarea y esperará a que termine y le dará el resultado. O puedes decir:
Task<Document> t = start getDoc();
Entonces, un lugar en este lugar donde se filtra la "plomería" es que si desea hacer un delegado a un asyncmétodo, debe saber usar un async delegatetipo. Entonces, en lugar de Funcusted debe decir AsyncFunc, y así sucesivamente. Aunque algún día ese tipo de cosas podría solucionarse mediante una mejor inferencia de tipos.
Otra pregunta es qué debería suceder si dice comenzar con un método ordinario (no asíncrono). Obviamente, un error de compilación sería la opción segura. Pero hay otras posibilidades.