在ContinueWith()块中使用await

我有以下代码:

var result = MessageBoxHelper.MsgBox .ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo) .ContinueWith((answer) => { if (answer.Result == MessageBoxResult.Yes) { Task asyncTask = ExecuteAsyncFunc(); //asyncTask.Unwrap(); ?? asyncTask.ContinueWith((a) => { // More }, TaskContinuationOptions.OnlyOnRanToCompletion); } }, TaskContinuationOptions.OnlyOnRanToCompletion); } 

像这样在其他地方调用:

 public async Task ExecuteAsyncFunc() { await CallAnotherAsyncFunction(); } 

我相信我需要调用Unwrap(),我试过,因为我在ContinueWith()块中调用await。 但是,当我取消注释时,我收到以下错误:

错误CS1929’Task’不包含’Unwrap’的定义,最好的扩展方法重载’TaskExtensions.Unwrap(Task)’需要’Task’类型的接收器

我是否需要在此上下文中使用Unwrap,如果是这样,我做错了什么?

简短的回答是使用await而不是ContinueWith

 var result = MessageBoxHelper.MsgBox.ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo); if (answer == MessageBoxResult.Yes) { var a = await ExecuteAsyncFunc(); // More } 

答案很长的是, ContinueWith有一些危险的默认选项(包括使用环境TaskScheduler ) ,并且await自动为您正确地执行所有操作。 我在博客上详细介绍了一下。

我是否需要在此上下文中使用Unwrap,如果是这样,我做错了什么?

只有当您的返回类型是Task时才需要解包,并且您实际上想要对内部任务执行某些操作。

例如:

 Task exampleTask = Task.Factory.StartNew(async () => { await Task.Delay(1000); }); 

如果我想实际上异步等待内部Task ,我会调用Unwrapawait

 Task exampleTask = await Task.Factory.StartNew(async () => { await Task.Delay(1000); }).Unwrap(); 

如果由于任何原因你想要await从你的继续返回的任何任务,或者想要监视内部任务,那么你打电话给Unwrap 。 这里没有必要这样做。

你正在做的只是在你的ContinueWith调用异步方法,但似乎你不想await结果。

我建议尽可能使用async-await来减少ContinueWith的冗长。 将这两者混合在一起通常会产生不良结果,因为事情变得相当复杂。 重构代码最终更清晰,更少的圈复杂度:

 var result = await MessageBoxHelper.MsgBox.ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo); if (result == MessageBoxResult.Yes) { bool answer = await ExecuteFuncAsync(); } 

你说你因为在ContinueWith使用await而必须调用unwrap; 我实际上并没有看到你使用等待。 我认为你的意思是你想等待但不确定如何,因此需要解开思想。 如果是这种情况,那么你想要的只是使你的嵌套任务等待。 您可以通过使您提供的委托async

 var result = MessageBoxHelper.MsgBox .ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo) .ContinueWith(async (answer) => { if (answer.Result == MessageBoxResult.Yes) { await ExecuteAsyncFunc(); } }, TaskContinuationOptions.OnlyOnRanToCompletion); public async Task ExecuteAsyncFunc() { await CallAnotherAsyncFunction(); } 

这允许您在ContinueWith调用内等待委托内,而不必处理解包。

如果您打算对任务执行任何操作,例如在不打开的情况下返回它,那么这是正确的方法。

 public Task Foo() { return MessageBoxHelper.MsgBox .ShowAsync /* Continue with etc here */ } 

但是如果你要在Foo方法中对结果采取行动,那么就没有必要使用ContinueWith,只需等待它。

 public async Task Foo() { var result = await MessageBoxHelper.MsgBox .ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo); if (result == MessageBoxResult.Yes) { await ExecuteAsyncFunc(); return true; } return false; } 

这简化了你的代码安静一点。 另外需要注意的是,您的ExecuteAsyncFunc()方法不会对返回值起作用。 你只需等待,别无所求。

我注意到你没有返回true / false,所以我假设有更多的代码,为了清楚起见,你只是省略了。 如果不是这种情况,你可以节省一些开销,只需返回任务,允许有人进一步向上调用它来代替它。 如果您对CallAnotherAsyncFunction的调用返回Task那么您也可以以相同的方式返回该调用。 您只需要等待,如果您必须阻止该方法继续进行,以便您可以对等待方法的结果作出反应。 如果您不必,只需返回任务。

 public Task ExecuteAsyncFunc() { return CallAnotherAsyncFunction(); }