如何知道某个方法是否会抛出异常



我是Windows 8和c#开发的新手,但我对Java编程有一定的经验。

所以,当我尝试在java中制作一些Json解析器(例如)时,如果不使用try - catch块,我就无法做到这一点,这样我就可以处理异常,但是当我尝试在c# (Windows 8)中做同样的事情时,我不使用try - catch块,它也可以工作,像这样:

if (json != null)
{
JObject jObject = JObject.Parse(json);
JArray jArrayUsers = (JArray)jObject["users"];
foreach (JObject obj in jArrayUsers)
{
ListViewMainViewModel user = new ListViewMainViewModel(
(String)obj["email"],
(String)obj["token"],
(String)obj["institution"],
(String)obj["uuidInstitution"]);
usersList.Add(user);
}
return usersList;
}
}

我知道正确的方法是捕获JsonReaderException,但是Visual Studio从来没有警告过我。我想知道是否有一种简单的方法来知道是否有一些方法抛出异常,就像使用eclipse的java(这是强制性的实现try-catch块或代码不会编译)

您必须查阅文档。c#缺少throws关键字

你正在寻找的术语是检查异常,更多信息可以在c# FAQ中找到。

在c#中,你负责处理异常——在我看来,这是比Java实现更好的方式。实际上,异常应该是异常,也就是说:这不是您应该总是期望发生的事情

考虑这个奇怪的(但常见的)反模式:

try {

} catch (Exception ex) { /* Handler goes here */ }

这到底是什么意思?你真的要处理每一个经过这里的异常吗?甚至像OutOfMemoryExceptions这样的东西?这是坚果。这种模式唯一会导致的是抑制那些真的应该导致应用程序崩溃的合法异常——这与Java的方法非常相似。

Exception想象成一个标志,告诉程序员"嘿,环境刚刚进入一个不可能的状态"。例如,如果我试着除以0,系统抛出了一个DivideByZeroException,那么系统应该提醒你,因为这是一个故障——一个系统不能仅仅"找出它的出路"——如果你只是抑制这个问题,这真的有什么帮助吗?最后,这是适得其反的,因为您所做的一切都是在掩盖真正不可能的应用程序状态。如果您在应用程序中经常这样做,那么它最终会变成一堆有毒的错误。讨厌的东西!

异常也占用了大量的屏幕空间。有时我希望他们能让try/catch/finally块更精简一点,但后来我记得这样做会鼓励人们更多地使用它们,所以我很快就后悔了。

异常是程序员对程序员有用的通知,告诉你正在做的事情没有意义。显然,我们不应该将原始异常传递给用户,因为他们不知道如何处理这些异常。同时,你也不希望尝试处理地球上的每一个异常,因为在这种意义上的"处理"通常会转化为"抑制",这比让应用程序失败(优雅地)更糟糕。

c#,如前所述,没有受控异常,谢天谢地。

受控异常的概念表面上听起来很棒,但与任何因语言或运行时而被迫使用受控异常的人交谈,他们会说受控异常有三大问题:

  • 他们将自己的意愿强加给消费编码器。根据检查异常的定义,预计在将其抛出运行时之前对其进行处理。运行时实际上是在告诉程序员"当我抛出这个时,你应该知道该怎么做,所以就这么做"。首先,想一想;根据的定义,你被告知期望异常情况下发生的事情。其次,你需要以某种方式处理它。当你真正有能力解决异常所指出的问题时,这一切都很好。不幸的是,我们并不总是有这种能力,我们也不总是想做我们应该做的每件事。如果我正在编写一个执行数据转换的简单表单applet,并且我只想让我的应用程序在出现任何问题时死机,我不能什么都不捕获;我必须把所有可能抛出一些东西的方法的调用堆栈往上爬,并把它可能抛出的东西添加到抛出子句中(或者非常懒惰地添加一个抛出异常);子句对我的代码库的每个方法)。类似地,如果我的应用程序的构造使得我不能抛出一个特定的异常,也许是因为我正在实现一个超出我控制范围的接口,没有将其指定为潜在的可抛出对象,那么我唯一的选择就是完全吞下它,并返回一个可能无效的结果给我的调用者,或者将异常包装在一个未检查的可抛出类型中,如RuntimeException,并以这种方式抛出它(忽略整个检查异常机制)。当然,这是不推荐的)。

  • 它们违反了SOLID,特别是开闭原则。在代码中添加检查异常,如果不能处理该异常,则方法的所有用法要么处理该异常,要么将自己标记为抛出异常。重新抛出的用法必须由它们自己的调用者处理,或者它们必须被标记为抛出相同的异常。通过像在特定代码行中调用替代方法这样的外科手术式更改,您现在必须跟踪所有可能的调用堆栈,并对正常工作的代码进行其他更改,只是为了告诉他们您的代码可能会抛出异常。

  • 它们根据定义创建泄漏的抽象。调用者使用带有"throw"的方法;子句实际上必须知道有关其依赖项的这些实现细节。然后,如果它不愿意或不能处理这些错误,它必须将这些错误告知自己的消费者。当方法是接口实现的一部分时,问题变得更加复杂;为了使对象抛出该异常,接口必须将其指定为可抛出对象,即使不是所有实现都抛出该异常。

    Java通过Exception类的多级层次结构缓解了这一点;例如,所有与I/o相关的异常(应该是)IOException,并且具有与io相关目的的方法的接口可以指定可以抛出IOException,从而减轻指定每个特定子IOException的责任。然而,这带来的问题几乎和它解决的问题一样多;有许多ioexception,它们可能有非常不同的原因和解决方法。因此,你必须询问你在运行时捕获的每个IOException,以获得它的true类型(并且你很少或根本没有帮助识别可能被抛出的特定类型),以确定它是否可以自动处理,以及如何处理。

编辑:还有一个大问题:

  • 他们认为try-catch是处理可能的异常情况的唯一方法。假设你在c#中,在另一个世界中,c#有java风格的检查异常。您希望您的方法打开并读取由调用者传入的给定文件名的文件。像一个优秀的小程序员一样,您首先使用file验证文件是否存在于一个保护子句中。存在(永远不会抛出异常;为了返回true,路径必须有效,在路径上指定的文件必须存在,执行用户帐户必须至少对文件夹和文件具有读访问权限)。如果文件。Exists返回false,你的方法只是不返回数据,你的调用者知道该怎么做(假设这个方法打开一个包含可选配置数据的文件,如果它不存在,是空白或损坏,你的程序生成并使用默认配置)。

    如果文件存在,则调用file . open。好吧,文件。Open可以抛出九种不同类型的异常。但是它们都不可能发生,因为您已经使用File进行了验证。文件可以被运行程序的用户以只读方式打开。然而,受检查异常机制并不关心;您使用的方法指定它可以抛出这些异常,因此您必须处理它们或指定您自己的方法可以抛出它们,尽管您可能采取了一切预防措施来防止它。解决方法是将它们吞下并返回null(或者忘记guard子句,只捕获并处理File)。Open的异常),但这是您一开始试图用guard子句避免的模式。

这些甚至都没有考虑到潜在的邪恶。例如,开发人员可能会捕获未检查的异常并将其封装为已检查的异常(例如,捕获NullPointerException并抛出IOException),现在您必须捕获(或指定您的方法抛出)一个甚至不能很好地表示错误的异常。

至于在c#中使用什么代替,最佳实践是使用XML文档注释来通知使用您的方法的直接调用者可能会从该方法抛出异常。XML-doc在. net中相当于JavaDoc注释,使用方式大致相同,但语法不同(三个正斜杠后面是由XML标记系统包围的注释)。异常的标记很容易指定。为了有效地记录你的代码库,我推荐GhostDoc。但是,它只会为从被记录的方法内部显式抛出的异常生成异常注释,并且您必须填充一些空白。

我不是java开发人员,但从这里的答案来看,java实现似乎是这些方法的客户端的负担。然而,c#错过了一个机会(类似java或其他),它可以将可能发生的异常结果的类型传达给调用者,由方法的开发人员编写,允许调用者适当地处理它。

由于语言中没有内置此结构,因此我建议库开发人员采用包装器类,并将其用作任何可能出错的方法的返回类型。使用上述类作为返回类型而不是异常,客户机可以推断调用该方法时会发生什么,因为它在方法签名中有明确定义。此外,使用包装器将允许方法告诉客户端为什么出错,而不会像异常那样破坏流程。

关于这个主题的更多信息在这里:http://enterprisecraftsmanship.com/2015/03/20/functional-c-handling-failures-input-errors/

最新更新