一、異常的構成
new Exception() 創建異常對象
throw 拋出異常對象(主要性能損耗位置)
try{}catch{} 捕捉異常對象
C#里面異常對象分為兩個子類ApplicationException、SystemException,前者主要是提供給應用程序自定義的異常類,后者是提供給系統定義的異常類
二、如何使用異常
異常的正常邏輯是:由底層拋出,由高層處理
1、底層方法或者提供給其他人使用方法內的一些參數過濾或其它異常應該向上拋出
如(借鑒現成代碼懶得寫了):
public void InsertUser(User user) { if(user==null) { throw new ArgumentNullException("參數user為null"); } //調用Orm }
2、上層應該加上try{}catch{}來捕捉異常並處理
如:
try { bll.InsertUser(null); } catch (ArgumentNullException e) { LogHelper.Write(e.Message); }
3、避免一些錯誤的異常處理方式
如(借鑒現成代碼懶得寫了):
/* 用異常驗證用戶輸入 用戶輸入的合法性驗證是屬於業務邏輯的一部分,絕對不要用異常去處理,注意,是用戶輸入,所以這個經驗僅限於表現層邏輯 */ //典型錯誤1 try { int i=int.Parse(textBox1.Text); } catch(Exception ex) { alert(“不要輸入非數字”); } //典型錯誤2 void ValidateInput(int i) { if(i<0&&i>100) { throw new Exception("輸入數據范圍錯誤"); } } /* 將異常交由給底層處理 這個錯誤在於完全不驗證用戶輸入而直接把數據的驗證拋向數據庫,等待數據庫報錯來判斷用戶輸入的正確性,這個是非常致命的錯誤,很多注入漏洞都是由此產生的 */ //典型錯誤3 try { string name=Request.QueryString["xx"]; List<User> userls=User.QueryUserByName(name); } catch(SqlException ex) { } /* 完全不用異常機制 產生這個錯誤肯定是一個非常腦殘的決定造成的。不過很多時候某些不了解異常機制的人,由於對異常的性能開銷的恐懼感,經常會做出這么腦殘的決定 性能倒是高了,但是系統異常怎么辦呢?一旦數據庫出錯就只等着系統崩潰了。某些有經驗的說我會把下面的try{}catch{}起來,不過那不是脫了褲子放屁么,異常都拋出來了,開銷已經產生了,結果換來的是犧牲了異常對象的豐富信息而換來了畸形的系統邏輯。性能也沒得到提高 */ //典型錯誤4 public bool InsertUser(User user,ref int errcode) { if(user==null) { errcode=110;//參數為空錯誤的代碼 return false; } //調用Orm }
三、異常的性能測試
結論:
1、new Exception() 異常對象和創建普通對象的性能損耗沒太大差別
2、throw 拋出異常時會造成系統性能損耗,主要在於“填充運行的堆棧信息”、“記錄異常快照”等
3、try{}catch{}不會造成明顯的系統開銷,加不加性能區別不大
4、對外提供的方法盡可能拋出異常,避免程序崩潰,使用時請繼承ApplicationException基類
5、業務邏輯上的數據盡量用if{}else{}過濾掉,不要靠throw new Exception()來處理
6、異常在程序中無法避免,合理使用避免程序崩潰才是唯一正確的道路
(以上結論僅代表個人觀點)
public class TyhTest { private string msg; public TyhTest(string msg) { this.msg = msg; } } public class TyhException : ApplicationException { //這種寫法是繼承基類構造方法 public TyhException(string msg): base(msg) { } } class Program { static void Main(string[] args) { int max = 100; int j = 1; string str = "a"; //========== Stopwatch time1 = new Stopwatch(); time1.Start(); for (int i = 0; i < max; i++) { try { if (j > 0 && !string.IsNullOrEmpty(str)) throw new Exception("Exception異常"); } catch (Exception e) { } } time1.Stop(); Console.WriteLine("拋出Exception異常耗時:" + time1.ElapsedMilliseconds); //========== Stopwatch time2 = new Stopwatch(); time2.Start(); for (int i = 0; i < max; i++) { try { if (j > 0 && !string.IsNullOrEmpty(str)) throw new ApplicationException("Exception異常"); } catch (ApplicationException e) { } } time2.Stop(); Console.WriteLine("拋出ApplicationException異常耗時:" + time2.ElapsedMilliseconds); //========== Stopwatch time3 = new Stopwatch(); time3.Start(); for (int i = 0; i < max; i++) { try { if (j > 0 && !string.IsNullOrEmpty(str)) throw new TyhException("Exception異常"); } catch (Exception e) { } } time3.Stop(); Console.WriteLine("拋出自定義TyhException異常耗時:" + time3.ElapsedMilliseconds); //========== Stopwatch time4 = new Stopwatch(); time4.Start(); for (int i = 0; i < max; i++) { try { if (j > 0 && !string.IsNullOrEmpty(str)) { } else { } } catch (Exception e) { } } time4.Stop(); Console.WriteLine("不拋異常用IF判斷耗時:" + time4.ElapsedMilliseconds); //========== Stopwatch time5 = new Stopwatch(); time5.Start(); for (int i = 0; i < max; i++) { try { var test = new TyhTest("創建對象"); } catch (Exception e) { } } time5.Stop(); Console.WriteLine("創建普通對象TyhTest耗時:" + time5.ElapsedMilliseconds); //========== Stopwatch time6 = new Stopwatch(); time6.Start(); for (int i = 0; i < max; i++) { try { var test = new Exception("創建異常對象"); } catch (Exception e) { } } time6.Stop(); Console.WriteLine("創建異常對象Exception耗時:" + time6.ElapsedMilliseconds); Console.ReadKey(); } }
(Stopwatch 不需要 new 很多個,代碼復制粘貼的,請忽略,忽略,哈哈~)
