Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/email/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 是否需要一项任务<;T>;。转换<;TResult>;推广方法有用还是有隐患?_C#_Async Await_Task - Fatal编程技术网

C# 是否需要一项任务<;T>;。转换<;TResult>;推广方法有用还是有隐患?

C# 是否需要一项任务<;T>;。转换<;TResult>;推广方法有用还是有隐患?,c#,async-await,task,C#,Async Await,Task,我正在为Google Cloud API编写客户端库,这些API有一个非常常见的异步帮助器重载模式: 执行一些简短的同步工作来设置请求 发出异步请求 以简单的方式转换结果 目前我们正在使用异步方法,但是: 转换wait的结果在优先级方面很烦人-我们最终需要(wait foo.Bar().configurewait(false)).TransformToBaz(),括号很烦人。使用两个语句可以提高可读性,但这意味着我们不能使用表达式体方法 我们偶尔会忘记ConfigureAwait(fals

我正在为Google Cloud API编写客户端库,这些API有一个非常常见的异步帮助器重载模式:

  • 执行一些简短的同步工作来设置请求
  • 发出异步请求
  • 以简单的方式转换结果
目前我们正在使用异步方法,但是:

  • 转换wait的结果在优先级方面很烦人-我们最终需要
    (wait foo.Bar().configurewait(false)).TransformToBaz()
    ,括号很烦人。使用两个语句可以提高可读性,但这意味着我们不能使用表达式体方法
  • 我们偶尔会忘记
    ConfigureAwait(false)
    ——这在某种程度上可以通过工具解决,但还是有点异味
听起来是个好主意,但我读过反对它的书,理由似乎很合理。我们正在考虑为
任务添加扩展方法,如下所示:

潜在扩展方法

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);
}
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];
}
public async Task<TranslationResult> TranslateTextAsync(
    string text, string targetLanguage) =>
    (await TranslateTextAsync(new[] { GaxPreconditions.CheckNotNull(text, nameof(text)) }, targetLanguage)
        .ConfigureAwait(false))[0];
表达式体异步方法

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);
}
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];
}
public async Task<TranslationResult> TranslateTextAsync(
    string text, string targetLanguage) =>
    (await TranslateTextAsync(new[] { GaxPreconditions.CheckNotNull(text, nameof(text)) }, targetLanguage)
        .ConfigureAwait(false))[0];
公共异步任务TranslateTextAsync(
字符串文本,字符串目标语言)=>
(等待TranslateTextAsync(新[]{GaxPreconditions.CheckNotNull(text,nameof(text))},targetLanguage)
.ConfigureAwait(false))[0];
使用Convert的表达式体同步方法

public Task<TranslationResult> TranslateTextAsync(
    string text, string targetLanguage) =>
    TranslateTextAsync(new[] { GaxPreconditions.CheckNotNull(text, nameof(text)) }, targetLanguage)
        .Convert(results => results[0]);
公共任务TranslateTextAsync( 字符串文本,字符串目标语言)=> TranslateTextAsync(新[]{GaxPreconditions.CheckNotNull(text,nameof(text))},targetLanguage) .Convert(结果=>结果[0]);
我个人更喜欢最后一个

我知道这会改变验证的时间-在最后一个示例中,为
text
传递
null
值将立即抛出
ArgumentNullException
,而为
targetLanguage
传递
null
值将返回错误的任务(因为
TranslateTextAsync
将异步失败)。这是我愿意接受的区别

我是否应该注意日程安排或绩效方面的差异?(我们仍在构建两个状态机,因为
Convert
方法将创建一个状态机。使用
Task.ContineWith
可以避免这种情况,但存在博客文章中提到的所有问题。
Convert
方法可能会被更改为使用
ContinueWith

(我有点想在CodeReview上发表这篇文章,但我怀疑答案中的信息将比这是否是一个好主意更有用。如果其他人不同意,我很乐意将其删除。)

转换wait的结果在优先级方面会很烦人

我通常更喜欢引入一个局部变量,但正如您所指出的,它阻止了基于表达式的方法

我们偶尔会忘记
ConfigureAwait(false)
——这在某种程度上可以通过工具解决

由于您正在处理库,因此应使用
ConfigureAwait(false)
在任何地方,都值得使用代码分析器来执行
configurewait
用法。有一个和一个可以这样做。不过我自己还没有试过

Task.ContinueWith
听起来是个好主意,但我读了斯蒂芬·克利里(Stephen Cleary)的博客文章,建议不要这样做,理由似乎很合理

如果将
ContinueWith
,则必须明确指定
TaskScheduler.Default
(这是与等效的
ContinueWith
<代码> ConfigureAwait(false)< /> >,并考虑添加标记,如
denychildatach
。在我看来,很难记住如何使用
ContinueWith
正确地记住
配置等待(false)

另一方面,虽然
ContinueWith
是一种低级的、危险的方法,但如果您正确地使用它,它可以给您带来轻微的性能改进。特别是,使用
state
参数可以为您节省委派分配。这是TPL和其他Microsoft库通常采用的方法,但我认为它的效率较低这对于大多数库来说,可维护性太高了

它看起来如此简单和有用,以至于我有点惊讶于现在还没有可用的东西

您建议的
Convert
方法有。Stephen没有这样说,但我假设,承诺是任务等价物(它们是 两者都有)

另一方面,将这个概念带到一个有趣的地方 结论。
转换
/
是 用于在期货上实现LINQ。Stephen Toub也 (在这一点上有点过时,但很有趣)

我考虑过几次将
然后
添加到我的AsyncEx库中,
但每一次都没有成功,因为几乎一样
它的唯一好处是通过允许方法链接来解决优先级问题。我假设它不存在于
同样的原因

也就是说,实现您自己的应用程序肯定没有什么错
Convert
方法。这样做将避免使用括号/额外的局部 变量,并允许表达式体方法

我知道这会改变验证的时间

这是我的原因之一(我的博客文章探讨了更多的原因)

在这种情况下,我认为这两种方式都可以,因为“设置请求的简短同步工作”是一种先决条件检查,在我看来,抛出到哪里并不重要(因为它们无论如何都不应该被捕获)

如果“简短的同步工作”更复杂——如果它可以抛出,或者在一年后有人重构它之后可以合理抛出——那么我会使用
async
/
wait