標籤:winform style blog http io ar 使用 for sp
前言
這本書這幾年零零散散讀過兩三遍了,作為經典書籍,應該重複讀反覆讀,既然我現在開始寫博了,我也準備把以前覺得經典的好書重讀細讀一遍,並且將筆記整理到部落格中,好記性不如爛筆頭,同時也在寫的過程中也可以加深自己理解的深度,當然同時也和技術社區的朋友們共用
Tips
- vs調試catch塊時,監看式視窗變數: $exception 查看當前拋出的異常對象
- 異常的catch是自上而下,回溯調用棧,如果未找到,就拋出未處理異常
- 異常的執行順序:先執行body,再執行catch,最後執行finally
- 堆棧異常的執行順序:ParentBody ChildBody ChildCatch ChildFinally ParentCatch ParentFinally
- .NET 4版本及後續版本中,可向AppDomain的FirstChanceException登記Appdomain中發生的第一次異常捕獲,它發生在CLR搜尋任何catch塊之前
- finally中的代碼保證會被執行,無論異常是否發生(用Win32函數TerminateThread殺死線程,或者用Winb32函數TerminateProcess或System.Environment的FailFast方法殺死進程,finally塊不會執行。當然進程終止後,Windows會清理進程使用的所有資源)
- 如果catch或者finally中拋出異常,原有異常會丟失,新的異常繼續向上回溯
- CLR只能捕獲CLS相容(從Exception派生的異常被認為時CLS相容的)的異常,如果C#調用另一種程式設計語言的方法,拋出非CLS相容的異常,那麼C#代碼不能捕捉這個異常,這會有一些安全隱患
- 非CLS相容的一個異常被拋出時,CLR會自動構造RuntimeWrappedException類的執行個體,並初始化該執行個體的私人欄位,使之引用實際拋出的對象。這樣一來CLR就將非CLS相容的異常轉變成了CLS相容的異常。所以任何能捕捉Exception類型的代碼,都能捕捉非CLS相容的異常,從而消除了安全隱患
- 在C#2.0之後,Catch可以捕捉CLS相容和不相容的任何異常(Exception),而如果直接catch{} ,所以版本的C#都可以捕捉CLS相容和不相容異常
- 如果要相容CLR舊的行為[assembly:RuntimeCompatibility(WrapNonExceptionThrows=false)]
- 一個異常拋出時,CLR會在內部記錄throw指令的位置(拋出位置),一個catch捕捉到異常時,CLR又會記錄異常的捕捉位置
- 在catch塊內訪問被拋出的異常對象的StackTrace屬性,負責實現該屬性的代碼會調用CLR內部代碼,建立一個字串指出從異常拋出位置道異常捕捉位置的所有方法
- 在catch中重寫throw e會重設異常的起點(重設堆棧的起點),不符合編碼規範,如果僅僅使用throw 則OK
- 如果代碼方法被內聯,可能導致異常堆疊追蹤不準確對應到原始碼,如果要防止JIT最佳化內聯,使用編譯器開關 /debug 或者[System.Runtime.CompilerServices.MethodImplAttribute(MethodImplOptions.NoInling)]
- 異常自身字串訊息可以包含詳細的技術細節,用於跟蹤調試,但這些訊息不應該直接讓應用程式層捕獲,不應該向終端使用者顯示
- FCL的異常訊息都使用了本地化字串,開發人員可以根據實際情況考慮
- 設計和處理異常時,通常要犧牲可靠性來換取開發效率
- 盡量避免捕捉System.Exception然後允許應用程式繼續允許,一個很大的問題是狀態可能遭受破壞
- lock, using和foreach語句,都使用了try/finally塊,重寫析構器時,編譯器也會自動產生try/finally
- 非同步編程模型中異常會被“吞噬”然後重新拋出一樣的異常
- 編碼建議,一般捕獲並處理異常之後,不要把它吞噬,單獨使用throw,拋出相同的異常
- 關於異常的實踐規範,可以好好思考一下,比如定義應用程式層異常、業務層異常、服務異常,通訊異常,然後將FCL的異常“吞噬”記錄日誌
- dynamic對象調用方法如果失敗,會拋出TargetInvocationException異常。最初拋出的異常會正常地在調用棧中向上傳遞。這是使用C#的dynamic基元類型代替反射的一個很好的理由。(如果使用反射來調用方法,方法內的異常不能被正常捕獲)
- 未捕獲未處理的異常會寫入Windows日誌中
- 分布式程式中,盡量少的暴露異常資訊,如果堆棧資訊暴露,可能伺服器中的資訊被泄露
- 調試異常時,關注VS菜單“調試”-“異常”,可以強制引發某些異常發生,防止被“吞噬”(即使它被Catch)
- 另外異常處理的效能影響,能避免就避免,比如,如果有Try首碼的方法,盡量使用Try首碼的方法(不過Try的方法依然可能會拋異常,比如style參數無效,會拋出ArgumentException,另外還有可能拋出OutMemoryException異常)
- 如果要在拋出非預期的異常時維護狀態,CER很有用。這種異常也稱非同步異常。CLR載入程式集時,在AppDomain的Load堆中建立一個類型對象,調用類型的靜態構造器,並將IL代碼JIT編譯成本地代碼。如果操作失敗,CLR拋出異常報告失敗。(很多隱式的非一致性的異常,導致無法預料)
- CER, PrepareConstrainedRegions很特別的方法,JIT編譯器如果發現一個try塊前調用這個方法,會提前編譯與try關聯的catch和finally塊中的代碼。JIT編譯器會載入任何程式集,建立任何類型對象,調用任何靜態構造器,並對任何方法JIT編譯。如果其中任何操作造成異常,這個異常會線上程進入try塊之前發生。JIT編譯器提前準備方法時,還會遍曆調用圖,前提時方法一個用了[ReliabilityContractAttribute]而且傳遞Consistency.WillNotCorruptState或Consistency.MayCorruptInstance枚舉成員。這是由於加入方法會損壞AppDomain或進程狀態,CLR便無法對狀態一致性做出保證
- 可以調用RuntimeHelper的PrepareMethod手動準備方法
- CER文章參考:http://www.cnblogs.com/Ninputer/archive/2006/06/30/439757.html
未捕獲的異常處理
- Winform, OnThreadException虛方法及Application的ThreadException事件
- WPF程式Application的DispatcherUnhandledExceptions和System.WIndows.Therading.Dispatcher的UnhandledException和UnhandledExceptionFilter事件
- 對於SIlverlight,System.Window.Application的UnhandledExceptoin事件
- ASP.NET程式,System.Web.HttpApplication的Error事件
- 對於WCF,System.ServiceModel.Dispatcher.ChnnelDispatche的ErrorHandlers屬性
契約式編程
有時間我好好整理一下這方面的東西,畢竟一種編程習慣的培養,需要慢慢積累去逐漸使用新的思維
參考文章:
http://www.infoq.com/cn/news/2009/02/Code-Contracts-.NET
http://www.cnblogs.com/lucifer1982/archive/2009/03/21/1418642.html
讀書筆記—CLR via C#異常和狀態管理