您的位置:首页 > 其它

.NET进行异常处理时的原则注意事项

2012-04-07 20:54 330 查看
本文导航:
.NET进行异常处理时的原则注意事项

写在前面
try...catch...finally
知道何时设置 Try/Catch 块。
不要把异常处理方法作为从函数中返回信息的手段
为那些不该被忽略的错误使用异常
不要抛出new Exception()
使用using
不要使用无结构错误处理机制
不用使用异常来管理业务逻辑
应该捕获指定的异常
不要仅仅捕获异常而不做任何处理,不便于将来维护
创建用户定义的异常
类的设计应使在正常使用中从不引发异常
总结

.NET进行异常处理时的原则注意事项

写在前面

异常处理对于一个优秀的程序和系统来说是不言而喻的,如果想要程序的可维护性和健壮性增加异常处理是逃不过的话题,虽然我做的还不好,但是我正在努力……

try...catch...finally

发生异常时,异常沿堆栈向上传递,每个 Catch 块都有机会处理它。Catch 语句的顺序很重要。将针对特定异常的 Catch 块放在常规异常 Catch 块的前面,否则编译器可能会发出错误。确定正确 Catch 块的方法是将异常的类型与 Catch 块中指定的异常名称进行匹配。如果没有特定的 Catch 块,则由可能存在的常规 Catch 块捕捉异常。
公共语言运行库捕捉 Catch 块没有捕捉的异常。根据运行库的配置,或者出现一个调试对话框,或者程序停止执行并出现一个包含异常信息的对话框。
finally 语句的目的是确保即使在引发异常的情况下也能立即进行必要的对象(通常是保存外部资源的对象)清理。
此类清理功能的一个示例是在使用后立即对 FileStream 调用 Close,而不是等待公共语言运行时对该对象进行垃圾回收,如下所示:
static void CodeWithoutCleanup()
{
    System.IO.FileStream file = null;
    System.IO.FileInfo fileInfo = new System.IO.FileInfo("C:\\file.txt");

    file = fileInfo.OpenWrite();
    file.WriteByte(0xF);

    file.Close();
}


为了将上面的代码转换为 try-catch-finally 语句,需要将清理代码与工作代码分开,如下所示。

static void CodeWithCleanup()
{
    System.IO.FileStream file = null;
    System.IO.FileInfo fileInfo = null;

    try
    {
        fileInfo = new System.IO.FileInfo("C:\\file.txt");

        file = fileInfo.OpenWrite();
        file.WriteByte(0xF);
    }
    catch(System.UnauthorizedAccessException e)
    {
        System.Console.WriteLine(e.Message);
    }
    finally
    {
        if (file != null)
        {
            file.Close();
        }
    }
}


因为在 OpenWrite() 调用前,try 块内随时都有可能发生异常,OpenWrite() 调用本身也有可能失败,所以我们无法保证该文件在我们尝试关闭它时处于打开状态。 finally 块添加了一项检查,以确保在调用 Close 方法前,FileStream 对象不为 null。 如果没有 null 检查,finally 块可能引发自身的 NullReferenceException,但是应当尽可能避免在 finally 块中引发异常。
在 finally 块中关闭数据库连接是另一个不错的选择。 因为有时候数据库服务器允许的连接数是有限的,所以应尽快关闭数据库连接。 在由于引发了异常而无法关闭连接的情况下,使用 finally 块也是比等待垃圾回收更好的一种选择。
public int ExecuteNonQuery(string sql)
        {
            int res;
            try
            {
                cmd = new SqlCommand(sql, GetConn());
                res = cmd.ExecuteNonQuery();
            }
            catch (Exception)
            {

                throw;
            }
            finally
            {
                if (conn.State==ConnectionState.Open)
                {
                    conn.Close();
                }
            }
            return res;

        }


知道何时设置 Try/Catch 块。

例如,可以以编程方式检查可能发生的条件,而不使用异常处理。在其他情况下,使用异常处理捕捉错误条件是适当的。

下面的示例使用 if 语句检查连接是否关闭。如果连接未关闭,可以使用此方法而不是引发异常。

if(conn.State != ConnectionState.Closed)
      conn.Close();


在下面的示例中,如果连接未关闭,则引发异常。
try {
     conn.Close();
   }
   catch(InvalidOperationException ex) {
     //Do something with the error or ignore it.
   }


具体选择的方法依赖于预计事件发生的频率。如果事件确实是异常的并且是一个错误(如意外的文件尾),则使用异常处理比较好,因为正常情况下执行的代码更少。如果事件是例行发生的,使用编程方法检查错误比较好。

不要把异常处理方法作为从函数中返回信息的手段

这是一个极差的设计。不仅异常的处理缓慢(就像名字暗示的一样,他们意味着只被使用在异常情况),而且代码中许多的try/catch模块会导致代码很难维护。恰当的类设计可以提供普通的返回值。如果你确实在危机中想返回数据作为一个异常,那么你的方法可能做了太多的工作需要分解。

为那些不该被忽略的错误使用异常

不要抛出new Exception()

例如

如果根据对象的当前状态,属性集或方法调用不适当,则引发 InvalidOperationException。
如果传递的参数无效,则引发 ArgumentException 或从 ArgumentException 派生的类。

使用using

using作为语句,用于定义一个范围,在此范围的末尾将释放对象。

例如
public int ExecuteNonQuery(string sql, SqlParameter[] paras)
        {
            int res;
            using (cmd = new SqlCommand(sql, GetConn()))
            {
                cmd.Parameters.AddRange(paras);
                res = cmd.ExecuteNonQuery();
            }
            return res;
        }


不要使用无结构错误处理机制

类似于On Error Goto。

不用使用异常来管理业务逻辑

该使用条件语句。如果一个控制逻辑可通过 if-else 语句来简单完成的,那就不用使用异常,因为异常会降低代码的可读性和性能

应该捕获指定的异常

不要只是catch(Exception e) 了事,捕获指定的异常对性能、代码的可读性以及诸多方面都有好处

不要仅仅捕获异常而不做任何处理,不便于将来维护

创建用户定义的异常

创建自定义异常至少使用三个公共构造函数。

在下面的示例中,从 Exception 派生了一个新异常类 EmployeeListNotFoundException。该类中定义了三个构造函数,每个构造函数使用不同的参数。
Imports System
Public Class EmployeeListNotFoundException
   Inherits Exception

   Public Sub New()
   End Sub 'New

   Public Sub New(message As String)
      MyBase.New(message)
   End Sub 'New

   Public Sub New(message As String, inner As Exception)
      MyBase.New(message, inner)
   End Sub 'New
End Class 'EmployeeListNotFoundException


当创建用户定义的异常时,必须确保异常的元数据对远程执行的代码可用,包括当异常跨应用程序域发生时。例如,假设应用程序域 A 创建应用程序域 B,后者执行引发异常代码。应用程序域 A 若想正确捕获和处理异常,它必须能够找到包含应用程序域 B 所引发的异常的程序集。如果包含应用程序域 B 引发的异常的程序集位于应用程序域 B 的应用程序基目录下,而不是位于应用程序域 A 的应用程序基目录下,则应用程序域 A 将无法找到异常,公共语言运行库将引发 FileNotFoundException。为避免此情况,可以两种方式部署包含异常信息的程序集:
将程序集放在两个应用程序域共享的公共应用程序基中
- 或 -
如果两个应用程序域不共享一个公共应用程序基,则用强名称给包含异常信息的程序集签名并将其部署到全局程序集缓存中。

类的设计应使在正常使用中从不引发异常

例如,FileStream 类公开另一种确定是否已到达文件末尾的方法。这避免了在读取超过文件尾时引发的异常。下面的示例显示如何读到文件尾。
class FileRead {
    public void Open(FileStream fileToRead)
    {

        // This if statement is optional
        // as it is very unlikely that
        // the stream would ever be null.
        if (fileToRead == null)
        {
            throw new System.ArgumentNullException();
        }

        int b;

        // Set the stream position to the beginning of the file.
        fileToRead.Seek(0, SeekOrigin.Begin);

        // Read each byte to the end of the file.
        for (int i = 0; i < fileToRead.Length; i++)
        {
            b = fileToRead.ReadByte();
            Console.Write(b.ToString());
            // Or do something else with the byte.
        }
    }
}


总结

异常处理是一门,很深的学问,合理的使用异常,使得程序的可维护性,健壮性增加,我的这篇文章只是总结了一些常见的异常处理应注意的情况。还有很多情况都是我没有接触或者暂时没有想起来的,总之尽可能让你的异常处理为你程序的可维护性和健壮性服务,而非成为程序的累赘。

欢迎访问原文:http://www.zblog.us/programing/net/exception-handling.html
| 赵杰的博客
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: