Comment écrire une méthode asynchrone avec un paramètre out?

Je veux écrire une méthode asynchrone avec un paramètre out , comme ceci:

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

Comment est-ce que je fais ceci dans GetDataTaskAsync ?

Vous ne pouvez pas avoir de méthodes asynchrones avec out parameters ref ou out .

Lucian Wischik explique pourquoi ce n’est pas possible sur ce thread MSDN: http://social.msdn.microsoft.com/Forums/en-US/d2f48a52-e35a-4948-844d-828a1a6deb74/why-async-methods-cannot-have parameters -ref-ou-out

Quant à savoir pourquoi les méthodes asynchrones ne prennent pas en charge les parameters de référence? (ou parameters ref?) C’est une limitation du CLR. Nous avons choisi d’implémenter des méthodes asynchrones de la même manière que les méthodes itératives – c’est-à-dire que le compilateur transforme la méthode en un object machine-état. Le CLR n’a aucun moyen sûr de stocker l’adresse d’un “paramètre de sortie” ou d’un “paramètre de référence” en tant que champ d’un object. Le seul moyen d’avoir des parameters de référence pris en charge serait si la fonction asynchrone était effectuée par une réécriture CLR de bas niveau au lieu d’une réécriture du compilateur. Nous avons examiné cette approche et celle-ci avait beaucoup à faire, mais en fin de compte, cela aurait été si coûteux que cela ne serait jamais arrivé.

Une solution de contournement typique pour cette situation consiste à faire en sorte que la méthode asynchrone renvoie un Tuple à la place. Vous pouvez réécrire votre méthode en tant que telle:

 public async void Method1() { var tuple = await GetDataTaskAsync(); int op = tuple.Item1; int result = tuple.Item2; } public async Task> GetDataTaskAsync() { //... return new Tuple(1, 2); } 

Vous ne pouvez pas avoir de parameters ref ou out dans les méthodes async (comme cela a déjà été noté).

Cela crie pour une certaine modélisation dans les données qui circulent:

 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 GetDataTaskAsync() { var returnValue = new Data(); // Fill up returnValue return returnValue; } 

Vous avez la possibilité de réutiliser votre code plus facilement, et il est beaucoup plus lisible que les variables ou les tuples.

Une caractéristique intéressante des parameters out est qu’ils peuvent être utilisés pour renvoyer des données même lorsqu’une fonction génère une exception. Je pense que l’équivalent le plus proche de faire cela avec une méthode async serait d’utiliser un nouvel object pour contenir les données auxquelles la méthode async et l’appelant peuvent se référer. Une autre façon serait de passer un délégué comme suggéré dans une autre réponse .

Notez qu’aucune de ces techniques n’aura le même effet que le compilateur. Par exemple, le compilateur ne vous demandera pas de définir la valeur de l’object partagé ou d’appeler un délégué transmis.

Voici un exemple d’implémentation utilisant un object partagé pour imiter ref et out pour une utilisation avec des méthodes async et d’autres scénarios variés où ref et out ne sont pas disponibles:

 class Ref { // Field rather than a property to support passing to functions // accepting `ref T` or `out T`. public T Value; } async Task OperationExampleAsync(Ref 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(); // 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."); } } 

Alex a fait un excellent point sur la lisibilité. De manière équivalente, une fonction est aussi suffisamment interface pour définir le ou les types renvoyés et vous obtenez également des noms de variables significatifs.

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

Les appelants fournissent un lambda (ou une fonction nommée) et intellisense aide en copiant le nom de la variable du délégué.

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

Cette approche particulière est comme une méthode “Try” où myOp est défini si le résultat de la méthode est true . Sinon, vous ne vous souciez pas de myOp .

La solution C # 7 + doit utiliser la syntaxe tuple implicite.

  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." })); } 

return result utilise les noms de propriété définis par la signature de méthode. par exemple:

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

Je pense que l’utilisation de ValueTuples comme ça peut fonctionner. Vous devez d’abord append le package 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): } 

Voici le code de la réponse de @dcastro modifié pour C # 7.0 avec des tuples nommés et une déconstruction de tuple, ce qui simplifie la notation:

 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); } 

Pour plus de détails sur les nouveaux tuples nommés, littéraux tuple et déconstructions de tuple, voir: https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/