without whenall method httpresponsemessage explained ejemplo create await async c# async-await

whenall - task httpresponsemessage c#



¿Cómo escribir un método asíncrono sin parámetro? (7)

Quiero escribir un método asíncrono con un parámetro de out , como este:

public async void Method1() { int op; int result = await GetDataTaskAsync(out op); }

¿Cómo hago esto en GetDataTaskAsync ?


Alex hizo un gran punto sobre la legibilidad. De manera equivalente, una función también es la interfaz suficiente para definir los tipos que se devuelven y también se obtienen nombres de variables significativos.

delegate void OpDelegate(int op); Task<bool> GetDataTaskAsync(OpDelegate callback) { bool canGetData = true; if (canGetData) callback(5); return Task.FromResult(canGetData); }

Las personas que llaman proporcionan un lambda (o una función nombrada) y intellisense ayuda copiando los nombres de las variables del delegado.

int myOp; bool result = await GetDataTaskAsync(op => myOp = op);

Este enfoque particular es como un método "Try" donde myOp se establece si el resultado del método es true . De lo contrario, no te importa myOp .


Aquí está el código de la respuesta de @ dcastro modificado para C # 7.0 con tuplas y deconstrucción de tuplas nombradas, que agiliza la notación:

public async void Method1() { // Version 1, named tuples: // just to show how it works var tuple = await GetDataTaskAsync(); int op = tuple.paramOp; int result = tuple.paramResult; // Version 2, tuple deconstruction: // much shorter, most elegant (int op, int result) = await GetDataTaskAsync(); } public async Task<(int paramOp, int paramResult)> GetDataTaskAsync() { //... return (1, 2); }

Para obtener más información acerca de las nuevas tuplas, tuplas y deconstrucciones de tuplas, consulte: https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/


Creo que usar ValueTuples como este puede funcionar. Sin embargo, primero debe agregar el paquete ValueTuple NuGet:

public async void Method1() { (int op, int result) tuple = await GetDataTaskAsync(); int op = tuple.op; int result = tuple.result; } public async Task<(int op, int result)> GetDataTaskAsync() { int x = 5; int y = 10; return (op: x, result: y): }


No puede tener métodos asíncronos con out parámetros ref o out .

Lucian Wischik explica por qué esto no es posible en este subproceso de MSDN: http://social.msdn.microsoft.com/Forums/en-US/d2f48a52-e35a-4948-844d-828a1a6deb74/why-async-methods-cannot-have-ref-or-out-parameters

¿En cuanto a por qué los métodos asíncronos no admiten parámetros fuera de referencia? (¿o parámetros de referencia?) Esa es una limitación del CLR. Elegimos implementar métodos async de forma similar a los métodos de iteración, es decir, a través del compilador que transforma el método en un objeto máquina-estado. El CLR no tiene una manera 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 tener parámetros admitidos fuera de referencia sería si la función de sincronización 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 típica para esta situación es hacer que el método async devuelva un Tuple en su lugar. Puede volver a escribir su método como tal:

public async void 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); }


No puede tener parámetros ref o out en métodos async (como ya se señaló).

Esto exige modelar los datos que se mueven:

public class Data { public int Op {get; set;} public int Result {get; set;} } public async void Method1() { Data data = await GetDataTaskAsync(); // use data.Op and data.Result from here on } public async Task<Data> GetDataTaskAsync() { var returnValue = new Data(); // Fill up returnValue return returnValue; }

Usted gana la capacidad de reutilizar su código más fácilmente, además es mucho más legible que las variables o tuplas.


Una buena característica de out parámetros de out es que se pueden usar para devolver datos incluso cuando una función arroja una excepción. Creo que el equivalente más cercano a hacer esto con un método async sería usar un nuevo objeto para contener los datos a los que tanto el método async como el llamador pueden hacer referencia. 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 tiene. Es decir, el compilador no le pedirá que establezca el valor en el objeto compartido ni que llame a un delegado aprobado.

Aquí hay una implementación de ejemplo que utiliza un objeto compartido para imitar ref y out para usar con métodos async y otros varios escenarios donde ref y out no están disponibles:

class Ref<T> { // Field rather than a property to support passing to functions // accepting `ref T` or `out T`. public T Value; } async Task OperationExampleAsync(Ref<int> successfulLoopsRef) { var things = new[] { 0, 1, 2, }; var i = 0; while (true) { // Fourth iteration will throw an exception, but we will still have // communicated data back to the caller via successfulLoopsRef. things[i] += i; successfulLoopsRef.Value++; i++; } } async Task UsageExample() { var successCounterRef = new Ref<int>(); // Note that it does not make sense to access successCounterRef // until OperationExampleAsync completes (either fails or succeeds) // because there’s no synchronization. Here, I think of passing // the variable as “temporarily giving ownership” of the referenced // object to OperationExampleAsync. Deciding on conventions is up to // you and belongs in documentation ^^. try { await OperationExampleAsync(successCounterRef); } finally { Console.WriteLine($"Had {successCounterRef.Value} successful loops."); } }


La solución C # 7 + es usar sintaxis de tupla implícita.

private async Task<(bool IsSuccess, IActionResult Result)> TryLogin(OpenIdConnectRequest request) { return (true, BadRequest(new OpenIdErrorResponse { Error = OpenIdConnectConstants.Errors.AccessDenied, ErrorDescription = "Access token provided is not valid." })); }

El resultado de devolución utiliza los nombres de propiedad definidos por la firma del método. p.ej:

var foo = await TryLogin(request); if (foo.IsSuccess) return foo.Result;