如何知道某些方法是否可以抛出异常

Vic*_*rte 13 c# windows-8 windows-runtime

我是Windows 8和C#开发的新手,但我对Java Programming有一定的经验.

所以,当我尝试在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;
    }

}
Run Code Online (Sandbox Code Playgroud)

我知道正确的方法是捕获JsonReaderException,但Visual Studio从未警告过我.我想知道是否有一种简单的方法可以知道某个方法是否抛出异常,就像在使用eclipse的java上一样(它是强制实现try-catch块或代码不会编译)

Bar*_*chs 26

您必须参考相关文档.C#缺少throws关键字.

您正在寻找的术语是检查异常,更多信息可以在C#FAQ中找到.

  • 而且我很高兴它确实:) (11认同)

sir*_*lot 16

在C#中,您负责处理异常 - 恕我直言是比Java实现更好的方法.实际上,一个例外应该是非常特殊的,那就是:它不是你应该总是期望发生的事情.

考虑一下这种奇怪的(但是,常见的)反模式:

try {


} catch (Exception ex) { /* Handler goes here */ }
Run Code Online (Sandbox Code Playgroud)

这到底是什么意思呢?你真的要处理通过这里的每一个例外吗?甚至像OutOfMemoryExceptions 这样的东西?那太疯狂了.这种模式将导致的唯一问题是抑制真正应该降低应用程序的合法异常- 这与Java方法非常相似.

把它想象成一个Exception程序员的旗帜,说'嘿,环境刚刚进入一个不可能的状态'.例如,如果我尝试除以零并且系统抛出a DivideByZeroException,那么系统应该正确地提醒您,因为这是一个失败 - 一个系统不能只是'想象它的出路' - 如果你简单地压制这个问题,这真的有什么帮助?最后这会适得其反,因为你正在做的只是掩盖真正不可能的应用程序状态.如果你在你的应用程序中做了很多这样的事情,那么它最终只会变成有毒错误的污泥.呸!

例外也占用了大量的屏幕空间.有时候我希望他们能让这些try/catch/finally积木更加流线型,但后来我记得这样做会鼓励人们更多地使用它们,所以我很快就悔改了这个位置.

例外是有用的程序员到程序员通知,说你正在做的事情没有意义.显然,我们不应该将原始异常传递给用户,因为他们不知道如何处理它们.同时,你不想尝试处理地球上的每一个例外,因为在这种意义上的"处理"通常会转变为"抑制",这比让应用程序失败更加糟糕(优雅地) ).

  • 但应该注意的是(据我所知)Java并没有强迫你处理*异常,它只会强迫你*记录*异常.当API开发人员懒惰而忘记记录某些内容时,这可能非常方便.总而言之,我从来没有遇到过这个问题. (7认同)

Kei*_*thS 15

如前所述,C#没有检查异常,谢天谢地.

检查异常的想法听起来很棒,但是与任何被迫通过语言或运行时使用它们的人交谈,他们会说有三个大问题,检查异常:

  • 他们将自己的意志强加给消费者.根据它们的定义,检查的异常应该在它们被抛出运行时之前被处理.运行时实际上告诉编码器"当我抛出它时你应该知道该怎么做,所以这样做".首先,考虑一下; 你会被告知要按照其定义来预期特殊情况下会发生的事情.其次,你应该以某种方式处理它.好吧,当你真正有能力解决异常所指出的问题时,这一切都很好.不幸的是,我们并不总是具备这种能力,也不总是想做我们应该做的一切.如果我正在编写一个执行数据转换的简单表单小程序,而我只是希望我的应用程序在遇到任何问题时死于火热的死亡,我不能不抓住任何东西; 我必须上升所有可能抛出一些东西的方法的所有可能的调用堆栈,并添加它可以抛出到throws子句(或者非常懒惰,并在我的代码库的每个方法上放置一个"throws Exception"子句).同样,如果构建我的应用程序使得我不能抛出特定的异常,也许是因为我实现了一个超出我控制范围的接口而没有将其指定为潜在的throwable,那么我唯一的选择是完全吞下它并将可能无效的结果返回给我的调用者,或者将异常包装在一个未经检查的可抛出类型(如RuntimeException)中并将其抛出(忽略整个已检查的异常机制,当然不建议这样做).
  • 他们违反了SOLID,尤其是开放封闭原则.进行更改以向代码添加已检查的异常,如果无法处理所述异常,则方法的所有用法都必须处理它或将自己标记为抛出异常.这再次引发惯例必须通过自己的调用者或处理它们必须被标记为抛出同样的异常.通过在特定的代码行中调用替代方法进行外观更改,您现在必须跟踪所有可能的调用堆栈并对正常工作的代码进行其他更改,只是告诉他们您的代码可能会抛出一个例外.
  • 根据定义,它们会产生漏洞抽象.使用带有"throws"子句的方法的调用者实际上必须知道有关其依赖性的这些实现细节.然后,如果它不愿意或无法处理这些错误,则必须告知其自己的消费者这些错误.当该方法是接口实现的一部分时,问题更加复杂; 为了让对象抛出它,接口必须将它指定为throwable,即使并非所有实现都抛出该异常.

    Java通过具有Exle类的多级层次结构来缓解这种情况; 例如,所有与I/O相关的异常(应该是)IOExceptions,以及具有与IO相关的方法的接口可以指定可以抛出IOException,从而减轻了指定每个特定子IOException的责任.然而,这导致几乎与它解决的问题一样多; 有几十个IOExceptions,可能有不同的原因和解决方案.因此,您必须询问在运行时捕获的每个IOException以获取其真实类型(并且您很少或根本没有帮助识别可能抛出的特定类型),以确定它是否可以自动处理,以及如何处理.

编辑:一个更大的问题:

  • 他们假设try-catch是处理可能的异常情况的唯一方法.假设您在C#中处于C#具有Java样式检查异常的备用Universe中.您希望您的方法打开并读取给定调用者传入其中的文件名的文件.就像一个好的小编码器一样,你首先使用File.Exists验证文件是否存在于一个guard子句中(它永远不会抛出异常;为了返回true,路径必须是有效的,路径中指定的文件必须存在,并且执行用户帐户必须至少具有对文件夹和文件的读取权限.如果File.Exists返回false,则您的方法只返回没有数据,并且您的调用者知道该怎么做(比如这个方法打开一个包含可选配置数据的文件,如果它不存在,是空白或已损坏,您的程序会生成并使用默认配置).

    如果文件存在,则调用File.Open.好吧,File.Open可以抛出九种不同类型的异常.但是它们都不可能发生,因为您已经使用File.Exists验证了该文件可以由运行该程序的用户以只读方式打开.但是,检查的异常机制并不关心; 您正在使用的方法指定它可以抛出这些异常,因此您必须处理它们或指定您自己的方法可以抛出它们,即使您可能采取一切预防措施来防止它.接下来的回答是吞下它们并返回null(或忘记保护子句,只是捕获并处理File.Open的例外),但这是你试图首先避免使用guard子句的模式.

这甚至都没有考虑到邪恶的可能性.例如,开发人员可能会将未经检查的异常捕获并封装为已检查的异常(例如,捕获NullPointerException并抛出IOException),现在您必须捕获(或指定您的方法抛出)异常甚至可以很好地代表什么是错的.

至于在C#中使用什么,最好的做法是使用XML文档注释来通知使用您的方法的直接调用者可能会从中抛出异常.XML-doc是与JavaDoc注释等效的.NET,并且以相同的方式使用,但语法不同(三个正斜杠后跟由XML标记系统包围的注释).异常标记很容易指定.为了有效地记录您的代码库,我推荐GhostDoc.它只会为正在记录的方法中显式抛出的异常生成异常注释,但是,您必须填写一些空白.

  • @piedar - 可能,但你可以使用互斥锁来防止这种情况(如果你控制两个进程).关键是你可能会采取一切预防措施,以避免出现异常,然后如果它发生,你可能希望应用程序死亡.检查异常并不能真正为您提供该选项,至少在没有确保调用堆栈的每一层都知道它可能发生的情况下也是如此. (2认同)