Quiero escribir un método asíncrono con un out
pará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 out
pará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 ref
o out
pará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);
}
Tuple
alternativa. Muy útil.Tuple
. : PNo puede tener
ref
niout
parámetros en losasync
mé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
myOp
se establece si el resultado del método estrue
. De lo contrario, no te importamyOp
.fuente
Una buena característica de los
out
pará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 unasync
método sería usar un nuevo objeto para contener los datos a los que tanto elasync
mé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
out
tiene. 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
ref
yout
para usar conasync
métodos y otros escenarios diversos donderef
yout
no están disponibles:fuente
Amo el
Try
patró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 losasync
métodos en una versión cuasi delTry
patrón.Enfoque 1: generar una estructura
Esto se parece más a un
Try
método de sincronización que solo devuelve un entuple
lugar de unbool
con unout
parámetro, que todos sabemos que no está permitido en C #.Con un método que devuelve
true
defalse
y nunca tira unaexception
.Enfoque 2: pasar los métodos de devolución de llamada
Podemos usar
anonymous
mé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
Try
patrón, pero establece losout
pará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
TPL
diseño? No hay tuplas La idea aquí es que usemos excepciones para redirigirContinueWith
a dos caminos diferentes.Con un método que arroja un
exception
cuando 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
ContinueWith
que manejaráTask.Exception
en su bloque lógico. Aseado, ¿eh?La mejor de las suertes.
fuente
ContinueWith
llamadas tiene el resultado esperado? Según tengo entendido, el segundoContinueWith
verificará 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
async
métodos que no aceptanout
parámetros se aplica solo a los métodos asíncronos generados por el compilador, estos declarados con laasync
palabra clave. No se aplica a los métodos asíncronos hechos a mano. En otras palabras, es posible crearTask
métodos de retorno que aceptenout
parámetros. Por ejemplo, digamos que ya tenemos unParseIntAsync
método que arroja, y queremos crear unoTryParseIntAsync
que no arroje. Podríamos implementarlo así:Utilizando el
TaskCompletionSource
y elContinueWith
método es un poco incómodo, pero no hay otra opción, ya que no podemos usar el cómodoawait
palabra 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. ATaskCompletionSource
todavía sería necesario para elout
parámetro. Es posible que elout
pará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
,GetRawDataAsync
yFilterDataAsync
que se llaman en sucesión. Elout
parámetro se completa al completar el segundo método. ElGetDataAsync
método podría usarse así:Esperar lo
data
anterior antes de esperarrawDataLength
es importante en este ejemplo simplificado, porque en caso de una excepción, elout
pará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