Je veux écrire une méthode asynchrone avec un out
paramètre, comme ceci:
public async void Method1()
{
int op;
int result = await GetDataTaskAsync(out op);
}
Comment faire cela GetDataTaskAsync
?
la source
Je veux écrire une méthode asynchrone avec un out
paramètre, comme ceci:
public async void Method1()
{
int op;
int result = await GetDataTaskAsync(out op);
}
Comment faire cela GetDataTaskAsync
?
Vous ne pouvez pas avoir de méthodes asynchrones avec des paramètres ref
ou out
.
Lucian Wischik explique pourquoi cela n'est pas possible sur ce fil MSDN: http://social.msdn.microsoft.com/Forums/en-US/d2f48a52-e35a-4948-844d-828a1a6deb74/why-async-methods-cannot-have -ref-or-out-paramètres
Quant à savoir pourquoi les méthodes asynchrones ne prennent pas en charge les paramètres out-by-reference? (ou paramètres de référence?) C'est une limitation du CLR. Nous avons choisi d'implémenter les méthodes asynchrones de la même manière que les méthodes itératrices - c'est-à-dire via le compilateur transformant la méthode en un objet machine à états. Le CLR ne dispose d'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 objet. La seule façon d'avoir pris en charge les paramètres out-by-reference serait si la fonction asynchrone était effectuée par une réécriture CLR de bas niveau au lieu d'une réécriture par le compilateur. Nous avons examiné cette approche, et elle avait beaucoup à offrir, mais elle aurait finalement été si coûteuse qu'elle ne se serait jamais produite.
Une solution de contournement typique pour cette situation consiste à demander à la méthode async de renvoyer un Tuple à la place. Vous pouvez réécrire votre méthode comme telle:
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
alternative. Très utile.Tuple
. : PVous ne pouvez pas avoir de paramètres
ref
ouout
dans lesasync
méthodes (comme cela a déjà été noté).Cela crie pour une certaine modélisation dans les données en mouvement:
Vous gagnez la possibilité de réutiliser votre code plus facilement, et il est bien plus lisible que les variables ou les tuples.
la source
La solution C # 7 + consiste à utiliser la syntaxe de tuple implicite.
return result utilise les noms de propriété définis par la signature de méthode. par exemple:
la source
Alex a fait un excellent point sur la lisibilité. De manière équivalente, une fonction est également une interface suffisante pour définir le ou les types renvoyés et vous obtenez également des noms de variables significatifs.
Les appelants fournissent un lambda (ou une fonction nommée) et intellisense aide en copiant le (s) nom (s) de variable du délégué.
Cette approche particulière est comme une méthode "Try" où
myOp
est définie si le résultat de la méthode esttrue
. Sinon, vous vous en fichezmyOp
.la source
Une fonctionnalité intéressante des
out
paramètres est qu'ils peuvent être utilisés pour renvoyer des données même lorsqu'une fonction lève une exception. Je pense que l'équivalent le plus proche de faire cela avec uneasync
méthode serait d'utiliser un nouvel objet pour contenir les données auxquelles laasync
méthode 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 type d'application du compilateur qui
out
a. Par exemple, le compilateur ne vous demandera pas de définir la valeur de l'objet partagé ou d'appeler un délégué passé.Voici un exemple d'implémentation utilisant un objet partagé à imiter
ref
etout
à utiliser avec desasync
méthodes et d'autres scénarios variés oùref
etout
ne sont pas disponibles:la source
J'adore le
Try
motif. C'est un modèle bien rangé.Mais c'est difficile avec
async
. Cela ne veut pas dire que nous n'avons pas de vraies options. Voici les trois approches de base que vous pouvez envisager pour lesasync
méthodes dans une quasi-version duTry
modèle.Approche 1 - Sortie d'une structure
Cela ressemble le plus à une
Try
méthode de synchronisation renvoyant uniquement untuple
au lieu d'unbool
avec unout
paramètre, ce qui, nous le savons tous, n'est pas autorisé en C #.Avec une méthode qui retourne
true
defalse
et ne jette unexception
.Approche 2 - Passer dans les méthodes de rappel
Nous pouvons utiliser des
anonymous
méthodes pour définir des variables externes. C'est une syntaxe intelligente, bien que légèrement compliquée. À petites doses, ça va.La méthode obéit aux principes de base du
Try
modèle mais définit lesout
paramètres à transmettre dans les méthodes de rappel. C'est fait comme ça.Approche 3 - utiliser ContinueWith
Et si vous n'utilisez
TPL
que la version conçue? Pas de tuples. L'idée ici est que nous utilisons des exceptions pour redirigerContinueWith
vers deux chemins différents.Avec une méthode qui lève un en
exception
cas d'échec. C'est différent de retourner un fichierboolean
. C'est un moyen de communiquer avec leTPL
.Dans le code ci-dessus, si le fichier n'est pas trouvé, une exception est levée. Cela invoquera l'échec
ContinueWith
qui géreraTask.Exception
dans son bloc logique. Neat, hein?Bonne chance.
la source
ContinueWith
appels a le résultat escompté? Selon ma compréhension, le secondContinueWith
vérifiera le succès de la première continuation, pas le succès de la tâche initiale.J'ai eu le même problème que j'aime en utilisant le modèle Try-method-pattern qui semble fondamentalement incompatible avec le paradigme async-await-paradigm ...
Ce qui est important pour moi, c'est que je peux appeler la méthode Try dans une seule clause if et que je n'ai pas à prédéfinir les variables de sortie avant, mais que je peux le faire en ligne comme dans l'exemple suivant:
J'ai donc proposé la solution suivante:
Définissez une structure d'assistance:
Définissez la méthode Try async comme ceci:
Appelez la méthode Try async comme ceci:
Pour plusieurs paramètres de sortie, vous pouvez définir des structures supplémentaires (par exemple AsyncOut <T, OUT1, OUT2>) ou vous pouvez renvoyer un tuple.
la source
La limitation des
async
méthodes n'acceptant pas deout
paramètres s'applique uniquement aux méthodes asynchrones générées par le compilateur, celles-ci déclarées avec leasync
mot - clé. Cela ne s'applique pas aux méthodes asynchrones artisanales. En d'autres termes, il est possible de créer desTask
méthodes de retour acceptant desout
paramètres. Par exemple, disons que nous avons déjà uneParseIntAsync
méthode qui lance, et que nous voulons créer une méthode qui ne lanceTryParseIntAsync
pas. Nous pourrions l'implémenter comme ceci:Utilisation de la
TaskCompletionSource
et laContinueWith
méthode est un peu maladroit, mais il n'y a pas d' autre option , car nous ne pouvons pas utiliser le pratiqueawait
mot - clé dans cette méthode.Exemple d'utilisation:
Mise à jour: si la logique asynchrone est trop complexe pour être exprimée sans
await
, elle pourrait être encapsulée dans un délégué anonyme asynchrone imbriqué. UnTaskCompletionSource
serait toujours nécessaire pour leout
paramètre. Il est possible que leout
paramètre puisse être complété avant la fin de la tâche principale, comme dans l'exemple ci-dessous:Cet exemple suppose l'existence de trois méthodes asynchrones
GetResponseAsync
,GetRawDataAsync
etFilterDataAsync
qui sont appelées successivement. Leout
paramètre est complété à la fin de la deuxième méthode. LaGetDataAsync
méthode pourrait être utilisée comme ceci:Attendre le
data
avant d'attendre lerawDataLength
est important dans cet exemple simplifié, car en cas d'exception, leout
paramètre ne sera jamais complété.la source
Je pense que l'utilisation de ValueTuples comme celle-ci peut fonctionner. Vous devez d'abord ajouter le package ValueTuple NuGet:
la source
Voici le code de la réponse de @ dcastro modifiée pour C # 7.0 avec des tuples nommés et une déconstruction de tuple, qui rationalise la notation:
Pour plus d'informations sur les nouveaux tuples nommés, les littéraux de tuple et les déconstructions de tuple, voir: https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/
la source
Vous pouvez le faire en utilisant TPL (bibliothèque parallèle de tâches) au lieu d'utiliser directement le mot-clé await.
la source