早期的C语言编程的过程中,错误信息一般都会使用ERROR_CODE的方式进行处理,遇到错误就定义一下错误代码,然后返回,这样的异常处理方式会比较零散,很容易出错,对于程序中间产生的BUG会比较难于跟踪,而且程序结构会比较复杂,不好维护。
  随着高级语言的发展,在C++中就引入了 EXCEPTION的理念,所有的异常都通过EXCETPION进行上抛,最终可以在程序的起点处被CATCH,然后进行处理,随后的高级语言中都引入了这个概念,在JAVA以及.NET中的C#、VB.NET等都使用了EXCEPTION抛出的方式进行异常处理。  我本人一直都做JAVA语言的开发,在JAVA语言的开发中,EXCEPTION的使用可以两种用途,一种可以进行异常信息的传递,另一种作用是可以进行程序流程的控制,关于程序流程控制的做法,在很多论坛上都有争议,说EXCEPTION的使用不应该用作程序流程的控制,但是在我很多年的JAVA开发中,发现使用EXCEPTION做流程控制的方法很好用,而且代码流程非常清晰、简洁。  当然,今天讨论的问题是C#的异常处理机制,不是JAVA的异常处理机制,所以言规正传,开始说C#的异常处理吧,因为C#晚于JAVA出现,所以作为参照,吸取了很多JAVA的优点,就像当初JAVA吸取C++优点一样,一方面吸取了精华,另一方面又对JAVA的不足做出了改进,所以C#看上去和JAVA的非常的相象。  在异常处理机制上,两种语言非常的相近,都使用了“try.......catch.......finaly”这样的模式,异常的自定义和抛出也非常相近,只要自定义异常派生自“Exception”类,抛出的时候使用“throw MyException("error message");”的方式进行抛出即可,但是在一点上两种语言采用了两种不一样的方式,这也是我对C#异常处理机制心存芥蒂的地方,在JAVA中,所有要抛出的异常都需要声明,如程序中有“FileNotFoundException”,要么将其使用try....catch语句进行捕获,直接处理,要么就在方法的定义时使用“throws MyException”声明抛出,让上面的程序处理,例如:import java.io.*;class MyClass { public MyClass () {} public void myMethod () { File file = new File ( "/home/my/aa.txt" );

}
}这段程序直接编译会无法通过,因为其中建立文件的语句有“FileNotFoundException”异常抛出,所以必须进行处理,要么捕获,要么抛出,所以有两种选择选择1:捕获、处理import java.io.*;class MyClass { public MyClass () {} public void myMethod () { try { File file = new File ( "/home/my/aa.txt" ); } catch ( FileNotFoundException fe ) {
System.out.println( fe.getMessage () );
}

}
}选择2:抛出import java.io.*;class MyClass { public MyClass () {} public void myMethod () throws FileNotFoundException { File file = new File ( "/home/my/aa.txt" );

}
}第一种选择的做法我想大家很明白,很多人都是这样做的,但是实际情况中这样做的时候不是很多,因为当程序开始复杂的时候就会根据角色进行分类处理,底层的模块一般都不会进行异常的处理,即使是捕获,也是进行封装后上抛,这样可以让所有的异常信息最终汇集为一点,这一点一般都存在在UI层上,UI层是整个程序的入口,只有在这里捕获的异常才会最终知道是如何进行处理,在上面例子中,捕获后就使用了“System.out.println”方法进行打印,但是实际情况中这样做是很不负责的,因为这条异常信息可能是要被写进日志,也可能是要被用作消息提示给用户,所以作为底层的模块,一般是不作最终处理的,所以一般大都会选择直接上抛或者是包装后上抛,也就是选择2,下面再说一种包装后上抛的例子,就是上面的例子,做如下改动:选择3:包装后上抛import java.io.*;class MyClass { public MyClass () {} public void myMethod () throws MyException{ try { File file = new File ( "/home/my/aa.txt" ); } catch ( FileNotFoundException fe ) {
throw new MyExeption( "F0001",fe );
}

}
}
这种实现机制中我们引入了一个自定义的Exception类,这个类既可以包含直接的异常信息,同时还可以包含其他异常的信息,在这里我们借鉴了以前C语言里的ERROR_CODE的做法,将所有的异常都编了号,最终的异常信息会写入到文本文件中,这样就可以进行多语言的开发,因为ERROR_CODE不包含直接错误信息,直接错误信息是在文本文件中,当需要开发另外一种语言的时候,只要翻译那个文本文件就行了。    说了这么长,好像还是没有说出C#的缺陷来,我们看上面的代码,在JAVA只要程序中调用了有异常抛出的方法,系统就会提示需要进行捕获或抛出,在“选择3”中如果其他方法调用这个方法,编译器就会提示需要捕获“MyException”,因为方法“myMehod”的声明中使用了“throws MyException ”,所以其他调用的程序都会提示这里有异常抛出,这样一来,不管程序写到何处,所有的异常都是已知的,系统都会提示需要捕获相应的异常,因为只要程序中有“throw”出现,就必须用“throws”在方法的定义中声明,“throw”和“throws”必须成对出现,这样就保证了没有异常被遗漏,提高了程序的安全性。   但是在C#中,这种规则被取消了,这是我一直都没有想通的事情,因为C#中的“throw”不需要使用“throws”进行声明,当方法被调用的时候就不会提示有异常需要捕获,这样就会为后面的程序埋下隐患。由于异常没有被声明,所以就不会被强制捕获,所以错误的捕捉只能靠测试来完成,如果没有测试到那中异常,那么就会忽略这个异常,这种情况将会变成后期的隐患,如以下这段代码:
using System.IO;namespace MyNameSpace
{
public class MyClass {
}

public void myMethod () {
try  { 
    string filepath=this.textBoxfilepath.Text; 
    long isize; 
    FileStream fs=File.Open(filepath,FileMode.Open); 
    isize=fs.Length; 
    fs.Close(); 
   } catch(UnauthorizedAccessException uex)  { 
   Console.WriteLine(uex.Message); 
   }   catch(FileNotFoundException fex)  { 
    Console.WriteLine(fex.Message); 
   }
}
}       在这段代码中,我们捕获了两个异常,针对这两个异常我们可以做相应的处理,但是如果我不写这段“try...catch”的话,编译器也不会报什么错,当然,这里最要命的是我们不知道需要捕获什么异常,这里的“UnauthorizedAccessException ”和“FileNotFoundException ”都是通过错误输入测试后得到的信息,这样才会知道有什么样的异常抛出,但是我这里可能测试做的不够,很多异常都可能会是未知的。 在C#的异常处理机制中,我觉得有一个很大的隐患,就是我们永远不知道会有什么样的异常抛出,因为系统从来都不会提示,我们很想写“try.....catch”,但是我们不知道要catch什么,所以很多情况下都出现了这种情况try {
.......................
........................
} catch ( Exception e ) {
.................
}这样就变成了眉毛胡子一把抓,没有什么针对性,大家也可能会说,没有关系,都抛上去,最终会由.net FrameWork统一处理的,但是我觉得这是一种不负责任的做法,因为异常信息很多是要给用户看的,比如我们在文件操作中会有一个FileNotFoundException,如果没有捕获处理,用户可能会看到一大串英文字母和堆栈信息,如果处理过的话,用户可能看到的就是“xxx文件不存在,请确定已经放置正确!”,所以我感觉这种不强制声明的异常处理做法隐患很多。    最近我也看到有人在讨论这个问题,大家吵来吵去,也不知道有什么样的结果,我今天写这些东西,也希望听听大家的意见,欢迎大家进行讨论。 
       

解决方案 »

  1.   

    codeproject有一个异常处理模块,个人感觉.net推荐统一扔上去处理。
      

  2.   

    >>> 我们很想写“try.....catch”,但是我们不知道要catch什么
    事实并不是这样
    MSDN文档中每个类型都注明了可能抛出的异常类型和原因>>> catch ( Exception e )
    >>> 这样就变成了眉毛胡子一把抓,没有什么针对性
    这在.NET也是不推荐的做法你的想法我同意
    但这不算C#的问题
      

  3.   

    http://www.25hoursaday.com/CsharpVsJava.html
      

  4.   

    TO: Sunmast(速马/MVP) 你说每一个方法可能抛出的异常都在MSDN中注明了,但是我不能每用一个方法都去查MSDN啊,在JAVA中是强制的,有异常抛出必须声明,这样就不需要再去查MSDN了不过也非常感谢你的回答,昨天我想明白了这个问题,其实如果不是为了容错的话,比如多试验几次网络操作,一般的异常都直接上抛就可以了,自己定义的错误就用自己的异常,因为如果其他的异常被抛到了最上面的话,其实也就是说明程序中有BUG或是系统环境发生了变化,这种错误一般都是开发错误或是运行错误,都需要开发人员和管理人员进行处理的,不需要用户进行处理
      

  5.   

    TO:  athossmth(athos) 非常感谢你的回帖,不过很抱歉,我这里讨论的不是C# VS JAVA的问题,因为这两种语言都会有自己的长项和缺陷,这个问题大家已经讨论的太多了,昨天只是就一个具体的实现模式提出了一点疑问,如果大家还有什么好的想法,欢迎回复。
      

  6.   

    using System.IO;namespace MyNameSpace
    {
    public class MyClass {
    }

    public void myMethod () {
    try  { 
        string filepath=this.textBoxfilepath.Text; 
        long isize; 
        FileStream fs=File.Open(filepath,FileMode.Open); 
        isize=fs.Length; 
        fs.Close(); 
       } catch(UnauthorizedAccessException uex)  { 
       Console.WriteLine(uex.Message); 
       }   catch(FileNotFoundException fex)  { 
        Console.WriteLine(fex.Message); 
       }
    }
    }       在这段代码中,我们捕获了两个异常,针对这两个异常我们可以做相应的处理,但是如果我不写这段“try...catch”的话,编译器也不会报什么错,当然,这里最要命的是我们不知道需要捕获什么异常,这里的“UnauthorizedAccessException ”和“FileNotFoundException ”都是通过错误输入测试后得到的信息,这样才会知道有什么样的异常抛出,但是我这里可能测试做的不够,很多异常都可能会是未知的。 在C#的异常处理机制中,我觉得有一个很大的隐患,就是我们永远不知道会有什么样的异常抛出,因为系统从来都不会提示,我们很想写“try.....catch”,但是我们不知道要catch什么,所以很多情况下都出现了这种情况try {
    .......................
    ........................
    } catch ( Exception e ) {
    .................
    }这样就变成了眉毛胡子一把抓,没有什么针对性,大家也可能会说,没有关系,都抛上去,最终会由.net FrameWork统一处理的,但是我觉得这是一种不负责任的做法,因为异常信息很多是要给用户看的,比如我们在文件操作中会有一个FileNotFoundException,如果没有捕获处理,用户可能会看到一大串英文字母和堆栈信息,如果处理过的话,用户可能看到的就是“xxx文件不存在,请确定已经放置正确!”,所以我感觉这种不强制声明的异常处理做法隐患很多。
    你既然在底层自己定义了异常并且抛出,那么在最上面就要对所有的自定义异常作捕获处理,再附加捕获系统异常。如果觉得上层捕获的异常不明确,最明显的问题是自定义的异常不够多或者在该用自定义异常处理的地方因为‘懒惰’而没用用。写太多的异常捕获代码可能是很多程序员不喜欢的事情,但那时必须的。
      

  7.   

    Java那是语法累赘。就算不写throws,编译器仍然可以分析出所有的方法可能抛出什么异常。但是我赞成你的说法,IDE应该告诉我们可能会抛出哪些异常。
    所以说throws是语法上的累赘,让人去做编译器的事情。不过我仍然认为应改通过别的途径告诉我们可能抛出哪些异常。至于你所说的头发胡子一把抓,在记录异常日志中也有用到,例如:catch ( Exception e )
    {
      Logs.Exception( e )
      throw;
    }
    用异常来控制流程则在.NET Framework中更是常见。
    Thread.Abort和Response.End都是利用异常来控制流程的。
      

  8.   

    >>> 用异常来控制流程则在.NET Framework中更是常见
    完全的不同意>>> Thread.Abort和Response.End都是利用异常来控制流程的
    乱讲
      

  9.   

    我知道你所说的是什么,Response.End的实现就是终止当前线程,不管终止线程进行了多少工作,避免其后面的代码被执行的办法还不就是扔一个ThreadAbortException异常出来..至少在我看来是.
      

  10.   

    Yes, I think IDE should tell us which exception we should catch. In my opinion, Java's exception is better than c#'s exception because we know which exception we should catch.I do not know who design .net exception and why, hehe. but now we have to accept it. just as we have to accept sql 92 standard. maybe it is good enough.
      

  11.   

    Yes, I think IDE should tell us which exception we should catch. In my opinion, Java's exception is better than c#'s exception because we know which exception we should catch.I do not know who design .net exception and why, hehe. but now we have to accept it. just as we have to accept sql 92 standard. maybe it is good enough.其实我觉得这段话也说得挺好的,试着翻译一下。是的,我也想IDE应该告诉我们应该捕获哪些异常。在我看来,Java的异常处理比C#的异常处理要好,因为我们知道哪些异常需要捕获。
    我不知道谁设计的.NET异常处理模块也不知道为什么,呵呵,但是现在我会接受它,就像我接受sql 92标准一样,或许这已经足够好了吧……
    顺便说一下,偶很反感这种中式英语。