public class Class1
{
public static void test()
{
try
{
test2();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static void test2()
{
try
{
test3();
}
catch(Exception ex)
{
throw new Exception("test2错误测试");
} }
public static void test3()
{
throw new Exception("test3错误测试");
}
}我想写抛出错误信息自己写的字符,但又不想每次都自定义错误类型。
上面的模拟代码存在一个问题,如果底层(错误字符自定义)抛出错误信息,上层(错误字符自定义)也抛出错误信息,
那么底层信息将被覆盖,要是上层的错误信息+ex.Message,有可能ex错误不一定是底层来的错误。
catch捕获错误信息能精确到类型,可不可以能捕获是本函数错误、还是别的函数的错误信息呢?
不知道那位前辈有更好的方法呢?谢谢!!
<1> 如果能够通过流程控制,原则上就不需要抛出异常
异常堆栈的跟踪需要相当的系统资源,对性能有不可忽视的影响。因此原则上能够通过流程控制的错误,就不需要抛出异常,常用的处理方式有Test-Doer模式和Try-Parse模式,都是以一个相对较小成本的操作代替相对较大成本的异常操作。
例如,用SqlDataReader读取数据的一段代码:
使用异常的代码:
try
{
while (dr.Read()) //the connection maybe closed
{
int id = int.Parse(dr["id"].ToString()); //the field mayby not integer
}
}
catch(SqlException sex) //从特殊到一般捕获和处理异常
{
//……
}
catch(InvalidOperationException ioex)
{
//……
}
catch(Exception ex)
{
ex.Data.Add("Msg", "读取数据错误。");
throw;//空触发
}
finally
{
//关闭连接
}
使用Test-Doer模式和Try-Parse模式的代码:
if (conn.State == ConnectionState.Open) //Test-Doer
{
while (dr.Read())
{
int id;
int.TryParse(dr["id"].ToString(), out id); //Try-Parse
}
}
<2> 操作失败时,只有不能预期的错误才应该抛出异常,否则用错误代码
异常和错误码,都能表示操作不能按照预期的目的完成功能。如果操作在普通的场景中都可能会抛出异常,暨引发异常的频率较高,或者说我们对异常能够比较准确的预期,那么我们不应该抛出异常,采用返回错误码更合适;反之,我们就应该抛出异常,表示运行超出我们的预期了,我们应该关注并试图改正它。
例如,SqlDataReader类的Read方法,读一行记录,能读是预期结果,不能读就表示有错误。对于读到最后一行而导致不能读的情况,是通过返回false的形式表示,而对于读的过程中数据库连接关闭了,则会抛出InvalidOperationException异常。
可以结合“使用异常传递错误信息”思考一下,现有系统中抛出的异常是否都合适。
<3> 选择最合适的异常类型表示错误,优先使用.net类库中的异常,只有根据类型需要特殊处理时才自定义使用新类型
一定要选择最合适异常抛出,抛出System.Exception总是错的,如果这么做了,那么就想一想自己是否真地了解抛出异常的原因。
如果.net类库没有需要的合适的异常类型,而我们需要根据异常进行特殊处理,那么我们就要考虑创建自己的异常类了。
一般情况下,自定义异常类应继承自Exception类型或基类型,重载基类型的构造函数,还可以为其添加自己的字段和属性并初始化。很重要的一点,所有的异常类型都应是可序列化的,只有这样异常对象才能在跨越应用程序域时得到封送处理,同时还能持久化至日志。
<4> 只有要转换类型才需要抛出新异常,否则要空触发
如果需要转换异常类型,才需要抛出新的异常,仅仅是附加一些信息,直接使用空触发即可,否则的话将会把原始的异常覆盖掉,不利于处理。上面使用异常结构读取SqlDataReader的代码就是一个例子。
<5> 不要让调用者决定是否抛出异常
调用者要比方法设计者更难以决定是否抛出异常。例如以下代码就是不合适的。
public Type GetType(string path, bool throwOnError)
{
//......
}
<6> 确保抛出异常提供丰富、准确、有意义而且语法恰当的错误消息
要注意的是这些信息是提供给谁的,可能是其它开发人员,也可能是最终用户,所以这些信息应根据面向的对象设计。异常消息中的每个句子都应该通顺清晰简洁,都应该使用逗号句号,而不要有问号和感叹号。
4.2. 异常处理的原则
<1> 只捕获处理特定的、能处理的异常,不能吞没异常
如果用catch语句块捕获了某个特定类型的异常,并完全理解在catch块之后继续执行对应用程序意味着什么,并对这种情况做出了适当的反应,那么我们说这种情况是对异常进行了处理。
如果捕获的异常具体类型不确定,并在不完全理解操作失败的原因或没有对操作失败做出反应的情况下让应用程序继续执行,那么我们说这种情况是把异常吞了。因此我们捕获System.Exception、System.SystemException、ApplicationException等这类型的不确定的异常总是一种不恰当的做法,虽然貌似没有问题,但是实际上存在风险,而且有可能会导致很严重的问题。
<2> 对于未捕获的异常,应该允许它向上传递,并有地方统一处理
不要捕获不应该捕获的异常。通常应允许异常沿堆栈向上传递,对于未捕获的异常,应该允许它向上传递,并有地方统一处理。
统一处理一般都是最顶层的异常处理程序,如Global中的Application_Error,它们通常负责处理一般的异常和未处理异常。
<3> 按照从特殊到一般的顺序捕获和处理异常
只捕获特定异常,只处理能处理的异常,也就要求我们按照从特殊到一般的顺序捕获和处理异常。一般不要捕获System.Exception等这类型的不确定的异常,除非打算重新抛出。上面使用异常结构读取SqlDataReader的代码就是一个例子。
<4> 要注意对异常的清理
对于精心编写的代码来说,try-finally的使用频率要比try-catch要高的多。这一点可能有违于直觉,因为有时可能会觉得:try不就是为了catch吗?要知道一方面我们要考虑程序状态的一致,另一方面我们还需要考虑资源的清理工作。
{}可以自定义Exception,让catch不同的异常类型,经分析后再抛出
之前在处理异常的时候通常只知道捕获,却不太关注这个异常的来源,楼主的问题还从来没考虑过呢