应该返回Task的方法抛出exception吗?
返回Task
的方法有两个报告错误的选项:
- 立刻抛出exception
- 返回将以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.Run
或Task.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的一点。 所以你的里程可能会有所不同……很多。 🙂