应该返回Task的方法抛出exception吗?

返回Task的方法有两个报告错误的选项:

  1. 立刻抛出exception
  2. 返回将以exception结束的任务

调用者是否应该期望两种类型的错误报告,或者是否有某些标准/协议将任务行为限制为第二个选项?

例:

 class PageChecker { Task CheckWebPage(string url) { if(url == null) // Argument check throw Exception("Bad URL"); if(!HostPinger.IsHostOnline(url)) // Some other synchronous check throw Exception("Host is down"); return Task.Factory.StartNew(()=> { // Asynchronous check if(PageDownloader.GetPageContent(url).Contains("error")) throw Exception("Error on the page"); }); } } 

处理这两种类型看起来很难看:

 try { var task = pageChecker.CheckWebPage(url); task.ContinueWith(t => { if(t.Exception!=null) ReportBadPage(url); }); } catch(Exception ex) { ReportBadPage(url); } 

使用async / await可能有所帮助,但有没有异步支持的纯.NET 4解决方案?

大多数Task -returning方法旨在与async / await (因此不应在内部使用Task.RunTask.Factory.StartNew )。

请注意,使用调用异步方法的常用方法,抛出exception的方式无关紧要:

 await CheckWebPageAsync(); 

差异只发生在调用方法然后等待的时候:

 List tasks = ...; tasks.Add(CheckWebPagesAsync()); ... await Task.WhenAll(tasks); 

但是,通常调用( CheckWebPagesAsync() )和await都在同一个代码块中,所以无论如何它们都在同一个try / catch块中,在这种情况下它也(通常)无关紧要。

是否有一些标准/协议将任务行为限制为第二种选择?

没有标准。 前置条件是一种类型的骨头exception ,所以它抛出的方式并不重要,因为它永远不会被捕获

Jon Skeet认为应该直接抛出前置条件(返回任务的“外部”):

 Task CheckWebPageAsync(string url) { if(url == null) // argument check throw Exception("Bad url"); return CheckWebPageInternalAsync(url); } private async Task CheckWebPageInternalAsync(string url) { if((await PageDownloader.GetPageContentAsync(url)).Contains("error")) throw Exception("Error on the page"); } 

这提供了与LINQ运算符很好的并行,它们保证像这样“早期”抛出exception(在枚举器之外)。

但我不认为这是必要的。 在任务中抛出前置条件时,我发现代码更简单:

 async Task CheckWebPageAsync(string url) { if(url == null) // argument check throw Exception("Bad url"); if((await PageDownloader.GetPageContentAsync(url)).Contains("error")) throw Exception("Error on the page"); } 

请记住, 永远不应该有任何捕获前置条件的代码 ,因此在现实世界中,抛出exception的方式不应该有任何区别。

另一方面,这我实际上不同意Jon Skeet的一点。 所以你的里程可能会有所不同……很多。 🙂