C# 是否有一个方法等效于async关键字?

C# 是否有一个方法等效于async关键字?,c#,async-await,C#,Async Await,有时,我想对异步方法的结果执行一些操作,例如: var foos = (await GetObjectAsync<IEnumerable<Foo>>()).ToList(); var result = (await (await GetObjectAsync<IEnumerable<Foo>>()).First().SomeMethodAsync()).GetResult(); 我想写一篇更流畅的文章,比如: var foos = GetObj

有时,我想对异步方法的结果执行一些操作,例如:

var foos = (await GetObjectAsync<IEnumerable<Foo>>()).ToList();
var result = (await (await GetObjectAsync<IEnumerable<Foo>>()).First().SomeMethodAsync()).GetResult();
我想写一篇更流畅的文章,比如:

var foos = GetObjectAsync<IEnumerable<Foo>>()
    .Async()
    .First()
    .SomeMethodAsync()
    .Async()
    .GetResult();
var foos=GetObjectAsync()
.Async()
.First()
.SomeMethodAsync()
.Async()
.GetResult();
我读过,但唯一有正确签名的东西(除非我遗漏了什么)是
Result
,而
Result
不是我想要的,因为它不是
wait
的等价物

这种方法存在吗?如果扩展不存在,我可以创建一个扩展来执行此操作吗?

您可以使用

var foos = GetObjectAsync<IEnumberable<Foo>>().GetAwaiter().GetResult().ToList()
var foos=GetObjectAsync().GetAwaiter().GetResult().ToList()
但这不一样! 如果使用“wait”关键字,则在等待结果时,将处理Windows Messageloop,并且不会冻结应用程序(因此会处理鼠标事件等)。 如果使用“GetAwaiter().GetResult”或“.Wait()”,则线程在等待时被阻塞


因此,“等待”并没有对等词。但是还有其他语法,看起来很相似,但行为却非常不同。

您正在寻找
ContinueWith

await GetObjectAsync<IEnumerable<Foo>>()
    .ContinueWith(task => task.Result.ToList());

要获得流畅的语法,可以使用以下扩展方法从
Task
中生成函子和单子:

public static async Task<TResult> Map<T, TResult>(this Task<T> source, Func<T, TResult> func)
{
    var result = await source.ConfigureAwait(false);

    return func(result);
}

public static async Task<TResult> Bind<T, TResult>(this Task<T> source, Func<T, Task<TResult>> func)
{
    var result = await source.ConfigureAwait(false);

    return await func(result).ConfigureAwait(false);
}
公共静态异步任务映射(此任务源,Func Func)
{
var result=await source.ConfigureAwait(false);
返回func(结果);
}
公共静态异步任务绑定(此任务源,Func Func)
{
var result=await source.ConfigureAwait(false);
返回等待函数(结果)。配置等待(错误);
}
这允许您以方法链的方式编写代码:

var resultTask = GetObjectAsync<IEnumerable<Foo>>()
    .Map(foos => foos.First())
    .Bind(foo => foo.SomeMethodAsync())
    .Map(r => r.GetResult());
var resultTask=GetObjectAsync() .Map(foos=>foos.First()) .Bind(foo=>foo.SomeMethodAsync()) .Map(r=>r.GetResult()); 当然,我在这里并没有考虑性能,但对于非性能关键的代码部分,我更喜欢可读性而不是微观优化,如果这有助于使代码更易于理解的话(在某些领域,我发现这种语法确实有帮助)

还要记住,很明显,您仍然会得到一个任务,因此在某个时候,您必须等待它,或者使用
结果
等待
进行阻止

方法链接也可以通过
ContinueWith
实现,如@Cory Nelson answer(小心地使用调度程序和选项的正确参数),避免创建异步状态机,但为了简单起见,我在扩展中使用了async和Wait,这是非常安全的


最重要的是,如果您将Map重命名为Select并绑定到SelectMany(并添加一个带有投影的重载),您也可以在任务上使用LINQ查询语法,尽管这可能不会引起您的兴趣,因为您正特别尝试从我所能读到的语法中删除该语法。

这是一个关键字,没有等效的方法。而且,在我看来,这是一件好事<代码>等待在幕后将代码转换为状态机,它不应该消失unnoticed@maccettura不可能编写一个方法,该方法接受一个
任务
并返回一个
T
,其行为与等待它的行为相同。这种方法的唯一可能实现是不返回实际结果,或者同步阻止(除非任务在调用这种方法之前完成)。@AlexanderDerck
其中
是一个关键字,并且有一个功能等价物(
其中
),因此,这似乎不是一个不合理的问题。这是不可行的,因为
wait
中断(大部分时间)当前方法的执行,并等待“回调”在其稍后离开的地方继续,因此同步方法等价物不可能存在。您可以在完成时链接continuation并映射
ToList
,但这仍然会返回一个任务,而不是T,然后您必须等待T或block。ToList是IEnumerable上的扩展,而不是Task上的扩展,因此您需要像Async(result=>result.ToList())这样的东西来返回任务或阻止调用来链接它。@Boiethios我通常有一个扩展方法来将任务变成functor:Async Task Map(此任务t,Func m)=>m(wait t t t t.CA(False));所以我可以做:GetOAsync().Map(r=>r.ToList())。有时,流畅的语法比async Wait更有意义。然后,在等待结果时,Windows Messageloop会被处理,应用程序不会被冻结,这只是使其异步化的一个副作用。这不是副作用,但这是引入“async”关键字的主要原因。为了获得与之前相同的行为,您需要编写类似“var task=task.start(..);while(!task.finished){Application.DoEvents();}”的代码,我希望有一个源代码来实现它。@gofal3的行为将不一样。这也不是异步的。谢谢你的回答,我来看看什么是
ContinueWith
,还有什么需要注意的地方。@Boiethios
ContinueWith
基本上是
任务选择(这个任务源,函数选择器)
如果听起来很熟悉的话:}
ContinueWith
的性能不会更差,它只需要大量的样板代码就可以使它真正正确地运行(您提到提供了一个调度程序,但它也不能正确地处理错误或取消)。您尚未实际写出的代码。@Servy有两个Task对象和continuations在continuateWithCode中,而wait中有一个。告诉我这样做的效果不会更差。@Boiethios您是指
Bind
调用的参数吗?如果SomeMethodAsync只是故意返回一个
任务
@Boiethios yes,那么在没有async/await的情况下,它就可以工作。我将变量名更改为
resultTask
,因此很明显结果不是T而是Task。但是,是的,当然你可以等待或做。结果得到T。哦,对不起,是的。我正在工作
var resultTask = GetObjectAsync<IEnumerable<Foo>>()
    .Map(foos => foos.First())
    .Bind(foo => foo.SomeMethodAsync())
    .Map(r => r.GetResult());