15

我是 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 块或代码不会编译)

4

4 回答 4

29

您将不得不为此查阅文档。C# 缺少throws关键字。

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

于 2013-08-19T14:40:22.600 回答
18

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

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

  • 他们将自己的意志强加给消费编码者。根据定义,已检查的异常应该在它们被抛出运行时之前被处理。运行时实际上是在告诉编码器“当我抛出这个时你应该知道该怎么做,所以这样做”。首先,考虑一下;你被告知期待在特殊情况下发生的事情案件的定义。其次,你应该以某种方式处理它。好吧,当您真正有能力解决异常指示的问题时,这一切都很好。不幸的是,我们并不总是有这种能力,也不是总是想做我们应该做的一切。如果我正在编写一个简单的表单小程序来执行数据转换,并且我只想让我的应用程序在出现任何问题时死去,我不能只是什么都抓不到;我必须查看每个可能抛出某些东西的方法的所有可能的调用堆栈,并将它可能抛出的东西添加到 throws 子句中(或者非常懒惰并在我的代码库的每个方法上放置一个“抛出异常”子句)。同样,如果我的应用程序是这样构建的,我不能抛出一个特定的异常,可能是因为我正在实现一个超出我控制范围的接口,它没有将它指定为潜在的 throwable,那么我唯一的选择是完全吞下它并将可能无效的结果返回给我的调用者,或者包装像 RuntimeException 这样的未经检查的可抛出类型中的异常并以这种方式将其抛出(忽略整个检查的异常机制,当然不建议这样做)。

  • 它们违反了 SOLID,尤其是开闭原则。进行更改以将检查的异常添加到您的代码中,如果您无法处理所述异常,则方法的所有用法都必须处理它或将自己标记为抛出异常。重新抛出的用法必须由它们自己的调用者处理,否则它们必须被标记为抛出相同的异常。通过像在特定代码行中调用替代方法一样进行更改,您现在必须跟踪所有可能的调用堆栈并对运行正常的代码进行其他更改,只是为了告诉他们您的代码可能会抛出一个例外。

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

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

编辑:还有一个大问题:

  • 他们认为 try-catch 是处理可能的异常情况的唯一方法。假设您在另一个宇宙中的 C# 中,其中 C# 具有 Java 样式的检查异常。您希望您的方法在给定由调用者传递给它的文件名的情况下打开并读取文件。像一个优秀的小程序员一样,您首先使用File.Exists验证文件是否存在于保护子句中(它永远不会抛出异常;为了返回 true,路径必须有效,路径指定的文件必须存在,并且执行用户帐户必须至少具有对文件夹和文件的读取权限)。如果 File.Exists 返回 false,则您的方法只是不返回任何数据,并且您的调用者知道该做什么(例如,此方法打开一个包含可选配置数据的文件,如果它不存在、为空白或已损坏,您的程序将生成并使用默认配置)。

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

这些甚至都没有考虑到邪恶的可能性。例如,开发人员可能会捕获未检查的异常并将其封装为已检查的异常(例如,捕获 NullPointerException 并抛出 IOException),现在您必须捕获(或指定您的方法抛出)不是的异常甚至可以很好地说明问题所在。

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

于 2013-08-19T23:17:36.720 回答
18

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

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

try {


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

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

Exceptiona 视为程序员的标志,上面写着“嘿,环境刚刚进入了不可能的状态”。例如,如果我尝试除以零并且系统抛出 a DivideByZeroException,那么系统应该正确地提醒您这一点,因为这是一个失败 - 系统不能只是“计算出它的出路” - 如果你简单地压制问题,这真的有什么帮助?最后,这会适得其反,因为您所做的只是掩盖真正不可能的应用程序状态。如果你在你的应用程序中经常这样做,那么它最终只会变成有毒的错误污泥。呸!

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

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

于 2013-08-19T14:41:53.533 回答
0

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

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

有关此主题的更多信息:http: //enterprisecraftsmanship.com/2015/03/20/functional-c-handling-failures-input-errors/

于 2017-07-26T21:36:50.700 回答