C#培训之零基础入门 11:异常和调试

一:什么是异常以及为什么要用异常

在异常机制出现之前,应用程序普遍采用返回错误代码的方式来通知调用者发生了异常。本节将首先阐述为什么要用抛出异常的方式来代替返回错误代码的方式。

对于一个成员方法而言,它要么执行成功,要么执行失败。成员方法执行成功的情况很好理解,但是如果执行失败了却并没有那么简单,因为我们需要将导致执行失败的原因通知调用者。抛出异常和返回错误代码都是用来通知调用者的手段。

假设我们要实现这样一个简单的功能:应用程序需要完成一次保存新建用户的操作。这是一个分布式的操作,保存动作除了需要将用户保存在本地外,还需要通过WCF在远程服务器上保存数据。

负责保存用户的成员方法如下:

private static int SaveUser1(User user)
{
    if (!SaveToFile(user))
    {
        return 1;
    }
    if (!SaveToDataBase(user))
    {
        return 2;
    }
    return 0;
}

如果单纯看此方法,似乎一切都还不错,在约定好了错误代码后,调用者只要接受到1或2,就知道到底是哪里出现了问题。但细究一下会发现,如果方法执行失败,似乎还可以挖掘出更多的原因。

在SaveToFile方法中,我们可能会遇到:

1 程序无数据存储文件写权限导致的失败;

2 硬盘空间不足导致的失败。

在SaveToServer方法中,我们可能会遇到:

1 服务不存在导致的失败;

2 网络连接不正常导致的失败。

当我们想要告诉调用者更多细节的时候,就需要与调用者约定更多的错误代码。于是我们很快就会发现,错误代码飞速膨胀,直到看起来似乎无法维护,因为我们总在查找并确认错误代码中。

采用接下来的方法,可能会省略掉很大一部分的错误代码:

private static bool SaveUser2(User user, ref string errorMsg)
{
    if (!SaveToFile(user))
    {
        errorMsg = "本地保存失败!";
        return false;
    }
    if (!SaveToDataBase(user))
    {
        errorMsg = "远程保存失败!";
        return false;
    }
    return true;
}

这看上去是不错,即使存在更多的错误也可以将失败信息呈现给调用者或者上层用户。然而,仅仅呈现失败信息就可以了吗?我们来看看这样一种情况:给失败通知增加稍微复杂一点的功能。

如果本地保存失败,要完成“通知运行本段代码的客户机管理员”的功能。通常情况下,仅仅只需要显示类似的信息:“本地保存失败,请检查用户权限”。 如果远程保存失败,应用程序需要“发送一封邮件给远程服务器的系统管理员”。这个增加的功能导致我们不能像处理“本地保存失败”那样来处理“远程保存失 败”。

一切仿佛又回到了起点,在没有异常处理机制之前,我们只能返回错误代码。但是,现在我们有了另一种选择,那就是使用异常机制。如果使用异常机制,那么最终的代码看起来应该是下面这样的:

static void Main(string[] args)
{
    try
    {
        SaveUser(user);
    }
    catch (IOException)
    {
        //IO异常,通知当前用户
    }
    catch (UnauthorizedAccessException)
    {
        //权限失败,通知客户端管理员
    }
    catch (CommunicationException)
    {
        //网络异常,通知发送e-mail给网络管理员。
    }
}

private static void SaveUser(User user)
{
    SaveToFile(user);
    SaveToDataBase(user);
}

使用CLR异常机制后,我们会发现代码变得更清晰、更易于理解了。

我们在使用try catch的时候,还常常会用到另外一个代码段finally,格式如下:

try
{
    //
}
catch (Exception error)
{
    //
}
finally
{
    //
}

finally是什么意思,也就是无论我们try中的这段代码是否抛出异常,finally中的语句都会被得到执行,这决定了finally非常适合用来做资源的释放,如我们打老鼠游戏中,有数据库读写操作,那么,读写操作后的数据库连接的关闭,就很适合用在这里。

以上部分摘自我的书《高质量代码编写:改善C#的157个建议》

 

二:调试

到了该介绍一些调试技巧的时候,在看前面的课程学习中,大家也看到了一些我针对程序出错,通过调试来找到错误点的时刻。在IT界流行着这样一个传说:程序不是写出来的,是调试出来的。先不说这话的是否正确,却也能看出调试在软件开发中的重要性。

本小节我们将介绍一些最简单的调试技巧,以便大家在接下来的编码中能运用起这个强大的武器,看视频:

Lesson11.wmv(我已在线发送给你)

备注:本文是课程《.NET C# 零基础入门》的免费部分,详细请参见TMJ .NET在线培训

本章知识点:

1:异常;

2:断点设置;

3:QuickWatch;

4:Exception.Message, Exception.StackTrace, Exception.InnerExcetion;

5:调用堆栈;

6:单步执行及Continue;

7:跳过执行代码或执行代码回退;

8:在调试暂停状态下修改变量的值;

posted @ 2013-02-13 17:02  Tony.J  阅读(201)  评论(0编辑  收藏  举报