Je suis en train d'écrire les bibliothèques client pour Google Api Cloud qui ont un motif assez commun pour async helper surcharges:
- Faire quelques brèves synchrone de travail pour définir une requête
- Faire une requête asynchrone
- Transformer le résultat d'une manière simple
Actuellement, nous sommes l'aide de méthodes asynchrones pour ça, mais:
- Transformer le résultat de l'attendent finit par être gênant en termes de préséance - nous finissons par avoir besoin d'
(await foo.Bar().ConfigureAwait(false)).TransformToBaz()
et les crochets sont ennuyeux. À l'aide de deux états améliore la lisibilité, mais signifie que nous ne pouvons pas utiliser une expression à corps de la méthode. - Parfois, nous oublions
ConfigureAwait(false)
- ce qui est soluble avec de l'outillage, dans une certaine mesure, mais il est encore un peu de l'odeur
Task<TResult>.ContinueWith
sonne comme une bonne idée, mais j'ai lu de Stephen Cleary du blog recommander contre elle, et les raisons semblent sonore. Nous envisageons d'ajouter une méthode d'extension pour Task<T>
comme ceci:
Le potentiel de la méthode d'extension
public static async Task<TResult> Convert<TSource, TResult>(
this Task<TSource> task, Func<TSource, TResult> projection)
{
var result = await task.ConfigureAwait(false);
return projection(result);
}
On peut alors appeler ce à partir d'une méthode synchrone vraiment très simplement, par exemple
public async Task<Bar> BarAsync()
{
var fooRequest = BuildFooRequest();
return FooAsync(fooRequest).Convert(foo => new Bar(foo));
}
ou encore:
public Task<Bar> BarAsync() =>
FooAsync(BuildFooRequest()).Convert(foo => new Bar(foo));
Cela semble si simple et utile que je suis un peu surpris de voir qu'il n'est pas quelque chose de déjà disponibles.
Comme un exemple de cas où je voudrais l'utiliser pour faire une expression corsé méthode de travail, en Google.Cloud.Translation.V2
code j'ai deux méthodes pour traduire du texte simple: on prend une chaîne unique et qu'on en prend plusieurs chaînes de caractères. Les trois options pour l'unique chaîne de version (simplifié quelque peu en termes de paramètres):
Régulière méthode async
public async Task<TranslationResult> TranslateTextAsync(
string text, string targetLanguage)
{
GaxPreconditions.CheckNotNull(text, nameof(text));
var results = await TranslateTextAsync(new[] { text }, targetLanguage).ConfigureAwait(false);
return results[0];
}
L'Expression des corps méthode async
public async Task<TranslationResult> TranslateTextAsync(
string text, string targetLanguage) =>
(await TranslateTextAsync(new[] { GaxPreconditions.CheckNotNull(text, nameof(text)) }, targetLanguage)
.ConfigureAwait(false))[0];
L'Expression des corps méthode de synchronisation à l'aide de Convertir
public Task<TranslationResult> TranslateTextAsync(
string text, string targetLanguage) =>
TranslateTextAsync(new[] { GaxPreconditions.CheckNotNull(text, nameof(text)) }, targetLanguage)
.Convert(results => results[0]);
Personnellement, je préfère le dernier de ceux-ci.
Je suis conscient que cela change au moment de la validation - dans le dernier exemple, le passage d'un null
valeur text
sera immédiatement jeter un ArgumentNullException
alors que le passage d'un null
valeur targetLanguage
sera de retour d'une reproché à la tâche (car TranslateTextAsync
échouent de manière asynchrone). C'est une différence, je suis prêt à accepter.
Existe t il des différences dans la planification ou de la performance que je devrais être au courant? (Nous sommes encore la construction de deux machines d'état, parce que l' Convert
méthode permet de créer un. À l'aide de Task.ContineWith
permettrait d'éviter que tous les problèmes mentionnés dans le billet de blog. L' Convert
méthode pourrait être modifié pour utiliser ContinueWith
attentivement.)
(Je suis un peu tenté de poster ceci sur CodeReview, mais je soupçonne que les informations contenues dans les réponses seront plus utiles en général au-delà de savoir si c'est une bonne idée. Si d'autres personnes sont en désaccord, je suis heureux de le déplacer.)