Quiero escribir un método asíncrono con un outparámetro, como este:
public async void Method1()
{
int op;
int result = await GetDataTaskAsync(out op);
}
¿Cómo hago esto GetDataTaskAsync?
fuente
Quiero escribir un método asíncrono con un outparámetro, como este:
public async void Method1()
{
int op;
int result = await GetDataTaskAsync(out op);
}
¿Cómo hago esto GetDataTaskAsync?
No puede tener métodos asincrónicos con refo outparámetros.
Lucian Wischik explica por qué esto no es posible en este hilo de MSDN: http://social.msdn.microsoft.com/Forums/en-US/d2f48a52-e35a-4948-844d-828a1a6deb74/why-async-methods-cannot-have -ref-or-out-parámetros
¿Por qué los métodos asíncronos no admiten parámetros de referencia? (¿o parámetros de referencia?) Esa es una limitación del CLR. Elegimos implementar métodos asincrónicos de manera similar a los métodos iteradores, es decir, a través del compilador transformando el método en un objeto máquina de estado. El CLR no tiene una forma segura de almacenar la dirección de un "parámetro de salida" o "parámetro de referencia" como un campo de un objeto. La única forma de admitir parámetros de salida por referencia sería si la función asincrónica se realizara mediante una reescritura CLR de bajo nivel en lugar de una reescritura del compilador. Examinamos ese enfoque, y tenía muchas posibilidades, pero en última instancia habría sido tan costoso que nunca hubiera sucedido.
Una solución alternativa típica para esta situación es hacer que el método asíncrono devuelva una Tupla. Podrías reescribir tu método como tal:
public async Task Method1()
{
var tuple = await GetDataTaskAsync();
int op = tuple.Item1;
int result = tuple.Item2;
}
public async Task<Tuple<int, int>> GetDataTaskAsync()
{
//...
return new Tuple<int, int>(1, 2);
}
Tuplealternativa. Muy útil.Tuple. : PNo puede tener
refnioutparámetros en losasyncmétodos (como ya se señaló).Esto grita un poco de modelado en los datos que se mueven:
Obtiene la capacidad de reutilizar su código más fácilmente, además es mucho más legible que las variables o las tuplas.
fuente
La solución C # 7 + es usar sintaxis implícita de tupla.
El resultado de retorno utiliza los nombres de propiedad definidos por la firma del método. p.ej:
fuente
Alex hizo un gran punto sobre la legibilidad. De manera equivalente, una función también es lo suficientemente interfaz como para definir los tipos que se devuelven y también se obtienen nombres de variables significativos.
Las personas que llaman proporcionan una lambda (o una función con nombre) e intellisense ayuda copiando los nombres de las variables del delegado.
Este enfoque particular es como un método "Probar" donde
myOpse establece si el resultado del método estrue. De lo contrario, no te importamyOp.fuente
Una buena característica de los
outparámetros es que pueden usarse para devolver datos incluso cuando una función arroja una excepción. Creo que el equivalente más cercano a hacer esto con unasyncmétodo sería usar un nuevo objeto para contener los datos a los que tanto elasyncmétodo como la persona que llama pueden referirse. Otra forma sería pasar un delegado como se sugiere en otra respuesta .Tenga en cuenta que ninguna de estas técnicas tendrá el tipo de aplicación del compilador que
outtiene. Es decir, el compilador no requerirá que establezca el valor en el objeto compartido o llame a un delegado pasado.Aquí hay una implementación de ejemplo usando un objeto compartido para imitar
refyoutpara usar conasyncmétodos y otros escenarios diversos donderefyoutno están disponibles:fuente
Amo el
Trypatrón Es un patrón ordenado.Pero, es un desafío con
async. Eso no significa que no tengamos opciones reales. Estos son los tres enfoques principales que puede considerar para losasyncmétodos en una versión cuasi delTrypatrón.Enfoque 1: generar una estructura
Esto se parece más a un
Trymétodo de sincronización que solo devuelve un entuplelugar de unboolcon unoutparámetro, que todos sabemos que no está permitido en C #.Con un método que devuelve
truedefalsey nunca tira unaexception.Enfoque 2: pasar los métodos de devolución de llamada
Podemos usar
anonymousmétodos para establecer variables externas. Es una sintaxis inteligente, aunque un poco complicada. En pequeñas dosis, está bien.El método obedece los conceptos básicos del
Trypatrón, pero establece losoutparámetros que se pasan en los métodos de devolución de llamada. Se hace así.Enfoque 3: use ContinueWith
¿Qué pasa si solo usa el
TPLdiseño? No hay tuplas La idea aquí es que usemos excepciones para redirigirContinueWitha dos caminos diferentes.Con un método que arroja un
exceptioncuando hay algún tipo de falla. Eso es diferente a devolver aboolean. Es una forma de comunicarse con elTPL.En el código anterior, si no se encuentra el archivo, se genera una excepción. Esto invocará la falla
ContinueWithque manejaráTask.Exceptionen su bloque lógico. Aseado, ¿eh?La mejor de las suertes.
fuente
ContinueWithllamadas tiene el resultado esperado? Según tengo entendido, el segundoContinueWithverificará el éxito de la primera continuación, no el éxito de la tarea original.Tuve el mismo problema que me gusta usar el patrón de método Try, que básicamente parece ser incompatible con el paradigma async-waitit ...
Importante para mí es que puedo llamar al método Try dentro de una sola cláusula if y no tengo que predefinir las variables out antes, pero puedo hacerlo en línea como en el siguiente ejemplo:
Entonces se me ocurrió la siguiente solución:
Defina una estructura auxiliar:
Defina el método de prueba asíncrono de esta manera:
Llame al método Try asíncrono de esta manera:
Para múltiples parámetros de salida, puede definir estructuras adicionales (por ejemplo, AsyncOut <T, OUT1, OUT2>) o puede devolver una tupla.
fuente
La limitación de los
asyncmétodos que no aceptanoutparámetros se aplica solo a los métodos asíncronos generados por el compilador, estos declarados con laasyncpalabra clave. No se aplica a los métodos asíncronos hechos a mano. En otras palabras, es posible crearTaskmétodos de retorno que aceptenoutparámetros. Por ejemplo, digamos que ya tenemos unParseIntAsyncmétodo que arroja, y queremos crear unoTryParseIntAsyncque no arroje. Podríamos implementarlo así:Utilizando el
TaskCompletionSourcey elContinueWithmétodo es un poco incómodo, pero no hay otra opción, ya que no podemos usar el cómodoawaitpalabra clave dentro de este método.Ejemplo de uso:
Actualización: si la lógica asincrónica es demasiado compleja para expresarse sin ella
await, entonces podría encapsularse dentro de un delegado anónimo asíncrono anidado. ATaskCompletionSourcetodavía sería necesario para eloutparámetro. Es posible que eloutparámetro se pueda completar antes de completar la tarea principal, como en el siguiente ejemplo:Este ejemplo supone la existencia de tres métodos asincrónicos
GetResponseAsync,GetRawDataAsyncyFilterDataAsyncque se llaman en sucesión. Eloutparámetro se completa al completar el segundo método. ElGetDataAsyncmétodo podría usarse así:Esperar lo
dataanterior antes de esperarrawDataLengthes importante en este ejemplo simplificado, porque en caso de una excepción, eloutparámetro nunca se completará.fuente
Creo que usar ValueTuples como este puede funcionar. Sin embargo, primero debe agregar el paquete ValueTuple NuGet:
fuente
Aquí está el código de la respuesta de @ dcastro modificado para C # 7.0 con tuplas con nombre y deconstrucción de tuplas, que simplifica la notación:
Para obtener detalles sobre las nuevas tuplas con nombre, tuples literales y deconstrucciones de tuplas, consulte: https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/
fuente
Puede hacerlo utilizando TPL (biblioteca paralela de tareas) en lugar de usar directamente la palabra clave await.
fuente