如何为新程序员讲授异常处理?[关闭]


21

您如何向程序员讲授异常处理。轻松教授所有其他内容-数据结构,ASP.NET,WinForms,WPF,WCF-您可以轻松地讲授所有内容。

使用异常处理,最终对它们进行尝试尝试只是异常处理的语法性质。

但是,应该教的是-您将代码的哪一部分放在try块中?你在赶路区做什么?

让我用一个例子来说明。

您正在处理Windows Forms Project(一个小型​​实用程序),并且已按照以下3个不同的项目进行了设计。

  1. UILayer
  2. 业务层
  3. 数据层

如果在DataLayer上引发了异常(让我们说加载XDocument会引发异常)(UILayer调用BusinessLayer,而BusinessLayer依次调用DataLayer),您是否只需执行以下操作

//In DataLayer
try {
    XDocument xd_XmlDocument = XDocument.Load("systems.xml");
} 
catch(Exception ex)
{
    throw ex;
}

哪些再次在BusinessLayer中引发,哪些在UILayer中捕获,在那里我将其写入日志文件?

这是您进行异常处理的方式吗?


15
如果你要做到这一点,你不想赶上(例外){抛出前; }-相反,只需捕捉{throw; }
史蒂文·埃弗斯

4
让我们忘记最后的方块吗?
克里斯,2010年

1
您应该在标签中指定语言。您将获得比大多数异常实现所共有的更多细节,而忽略块外的内容。例如,在C ++中,异常处理的最重要部分是知道如何编写异常安全程序。
David Thornley,2010年


我希望您知道捕获任何“异常”是一个坏主意。您应该具有嵌套的catch块来捕获特定的异常并进行相应的处理(而不仅仅是抛出异常)。
减去2013

Answers:


29

为了解释异常处理,请解释其背后的概念:经常发生错误的代码不知道如何正确处理该错误。 知道如何正确处理它的代码可以是调用该函数的函数,也可以在调用堆栈的更深处。

在编写调用可能引发异常的例程的例程时,如果您知道如何正确处理该错误,请将该调用放在try块中,并将错误处理代码放入catch块中。如果不是这样,请不要理会它,让调用堆栈中位于您上方的某些内容处理该错误。

说“ catch ex,throw ex” 不是进行异常处理的好方法,因为它实际上不处理任何事情。另外,根据您语言中的异常模型的工作方式,如果它清除了您本可以用来调试问题的堆栈跟踪信息,则实际上可能是有害的。只是让异常在调用堆栈中传播,直到它到达知道如何处理该例程的例程为止。


4
+1是“ ...,因为它实际上无法处理任何事情”,对异常处理不熟悉的人经常会认为捕获意味着对处理的理解,而没有意识到如果您不采取任何措施来解决问题,那不是异常处理,只是代码膨胀。
吉米·霍法

13

像大多数事物一样,对于新程序员来说,异常和异常处理似乎是寻找问题的一种解决方案,直到您证明为什么看似更简单的解决方案(C风格的返回码和errno)工作如此差劲。首先,我要激发问题并将其放在上下文中。展示如何使用返回码或全局/静态变量来完成错误处理。然后举例说明为什么它不能很好地工作。然后只有到那时,介绍异常并解释它们是带外信令的一种形式,其全部目的是,如果您忽略异常,则默认行为是将调用堆栈中的开销传递给可以处理它。

底线:展示如何在C语言中完成错误处理将使学生理解异常的真正含义,以及为什么捕获您无法真正处理的异常基本上是在模拟黑暗时代的处理方式。


2
+1指导他们使用传统的C风格的返回码和错误号,并向他们表明它的效果很差,因此教他们如何工作是一种绝妙的教学实践!
卡尼尼2010年

3
@卡尼尼:总的来说,我认为大多数相对较新的高层构造都像是解决问题的解决方案,如果您不了解它们要解决的问题以及发明的原因,那么它们很容易使用错误。
dsimcha

我同意,展示如何在没有异常的情况下进行操作是件好事,但随之而来的是解释何时使用异常以及何时使用其他技术的负担(因为并非所有情况都是例外)
Matthieu M.

@Matthieu:对。但是,如果您了解异常要解决的历史问题,而不是一味地了解异常,那么很明显,将异常用于异常情况是很愚蠢的。
dsimcha 2010年

是的,这就是为什么您获得了我的+1。我只是觉得您的回答可能被解释为“从不使用其他机制” :)
Matthieu M.

5

我将从“例外的设计准则”开始,它包括DO,DO NOT和AVOID。它还给出了原因。

在您的示例案例中,revelvent部分将是Wrapping Exceptions

并希望它是这样写的。请注意,它捕获特定的异常并尝试添加信息,以便传播更有意义的消息。另请注意,内部异常仍保留用于日志记录

//In DataLayer

try
{
XDocument xd_XmlDocument = XDocument.Load("systems.xml");
}
catch(FileNotFoundException ex)
{
        throw new TransactionFileMissingException(
                     "Cannot Access System Information",ex);
}

UPDATE Kanini询问在数据层中具有此异常块是否正确,或者检查文件是否可用于业务层。

首先,我想指出,包装异常的理由是

如果较低层异常在高层操作的上下文中没有意义,请考虑将从较低层抛出的特定异常包装在更合适的异常中。

因此,如果您认为更高层应该完全了解文件,那么数据层应该像这样

//In DataLayer

XDocument xd_XmlDocument = XDocument.Load("systems.xml");

不尝试不抓。

我个人认为,除非您的数据层可以做一些有用的事情,例如使用默认的systems.xml作为程序集资源,否则什么也不做或包装异常是一个不错的选择,因为您的日志记录将告诉您问题所在的方法和文件。(throw ex在这种情况下,还是首选对象throw也可以,但是不会增加任何值)。这意味着一旦确定,您将能够迅速解决问题。

顺便说一句,此特定示例还具有以下问题,因为XDocument.Load可能引发四个错误

  • ArgumentNullException
  • SecurityException
  • FileNotFoundException
  • UriFormatException

我们不能安全地保证以下代码不会抛出FileNotFoundException,这仅仅是因为当我们执行存在性检查时它可能在那里,而当我们进行加载时它就消失了。将其提供给业务层将无济于事。

 if (File.Exists("systems.xml")) 
     XDocument.Load("systems.xml");

SecurityException甚至更糟,因为除其他原因外,如果另一个进程被抢占时会抛出此异常,则它具有独占文件锁,直到您尝试打开它进行读取为止,因为没有File.CanIOpenThis()方法,您将不会收到错误。而且,如果存在这种方法,您仍然会遇到与File.Exists相同的问题。


修正:谢谢!但是在数据层中包含此异常块是否正确?检查文件是否可用的整个过程应该在业务层中吗?否则,我同意您编写代码的方法。
卡尼尼2010年

修正:在我的母语中,Kanini表示计算机,而Kani表示水果;-)
Kanini 2010年

我可以告诉您您对我的错误不是很沮丧,但是非常抱歉,我已经纠正了它。
康拉德·弗里克斯

1
修正:不高兴吗?一点也不。大大逗乐了。自从我向他指出这一点以来,我的兄弟一直没有停止笑,这主要是因为,除了可能形状怪异,我无论如何都不像水果……
Kanini 2010年

4

让我们扮演角色吧。(这不是一个笑话)

您应该参加一个讲习班,在其中讲解呼叫链。每个人都是一个对象。您将需要一些新手和一些了解“游戏”的人。

使用一个非常简单的问题,例如文件IO。gui->模型-> file_io

作为文件读取器的人需要告诉下一个。

首先用返回码来做。(使用便利贴吗?)

如果交互只是“代码所说的”,您很快就会使人们意识到异常是例外。

对于返回码,请传递便笺。

对于例外情况,请举手示意问题所在。

然后让他们执行“捕获x,抛出x”的操作,发现更糟的是诊断是GUI刚得到“模型有异常”。

我认为这将对您所拥有的人有所帮助,因为人们非常了解与他人的互动。


+1为角色扮演的想法。我们从来没有想过它。谁能想到可以通过角色扮演来完成教学编程?
卡尼尼(Kanini)2010年

1

我可以想象要了解异常,例如,您首先需要了解类的子/父关系。如果您了解孩子可以从父母那里继承功能,那么他们也许可以在基本层面上理解,如果孩子有问题,则无法解决,它将把这个问题(例外)传递给父母,并让父母处理用它。

这将变成一个链式关系,直到您最终找到一个知道如何处理异常的地方。

就最后而言,这是微不足道的部分……当问题发生时,必须处理某些事情,以使程序不会致命地退出,在处理完异常之后,那里的finally块将始终执行,而无论try catch是什么。

网络的一个很好的例子:

  • 我们建立联系
  • 连接正常,所以我们使用它
  • 完成后,我们关闭并释放资源

或在例外情况下:

  • 建立联系
  • 发生某种可以处理的异常
  • 在这一点上,我们释放了连接和相关的资源

1

将一个应用程序提供给新手,其中具有非常好的异常处理能力。将某些异常抛出到某个地方,让他们借助Logs对其进行调试。通过跟踪Exception的传播,他们应该能够对其进行调试。做这个运动3到4次。现在,只需从代码中删除所有异常处理,然后让他们尝试跟踪相同的异常即可。

我相信对异常处理代码的赞赏将立即得到赞赏。


听起来像是个计划。您是否会建议互联网上提供任何示例代码(例如,sourceforge.net)?
卡尼尼2010年

0

IMO,您应该认为异常处理和流控制语句基本相同。您可以使用它们来根据程序当前所处的条件来控制程序的流程。不同之处在于,仅当发生错误(或异常)时,异常处理才会做出反应。


@denny:虽然我同意“只有在发生错误(或异常)时例外处理才会作出反应”,但我对“例外处理和流控制语句基本相同”的说法不太确定。我谨对此表示不同意见。可以肯定的是,在这种情况下,catch块将执行其应做的工作。但是try块与流程或控制无关。最终的障碍还是与流程或控制无关。也许,我误解了您的答案,但是您能为我和他人的利益澄清一下吗?
卡尼尼2010年

0

这可能对新程序员没有帮助,但是当我开始在函数式编程中使用monad时,我发现对异常的理解要好得多。monad迫使您考虑数据可以通过其进入或离开程序的每个“通道”,因为它所做的一切都提供了方便的抽象来“隐藏”某些数据流。

函数可以具有不同类型的输出,并且异常类似于从函数返回的优先级较高的类型的想法非常巧妙。

提醒您,我知道这不是大多数语言(实现细节)如何工作的异常,但从抽象的意义上讲,这就是发生的事情。


0

假装猴子正在使用键盘

我曾经告诉我的人何时编写代码,以假装猴子将坐在键盘上并使用此应用程序。

这教会了他们如何预料各种事情:

  • 缺失数据
  • 遗失档案
  • 需要数字时的字母字符
  • 被零除

我认为这是只猴子敲敲键然后做任何想做的事情,而不是随随便便做到的窍门。它为我工作。


猴子?我想,您的企业用户从未听说过;-)
Kanini 2010年

@卡尼尼-好。那是在我的海军陆战队时代。我只是想让我的家伙在错误陷阱方面跳出思维框。我只是说错误陷阱吗?我的意思是异常处理。
迈克尔·赖利
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.