常见的C#异常及其修复方法
常見的C#異常及其修復方法
如果您今天是依靠編寫的軟件來謀生,那么您可能至少對異常的概念很熟悉。
Jeff Atwood曾經稱它們為“現代編程語言的基礎”。異常[1]是現代軟件開發中常見且有用的結構,但有時它們也可能造成混亂。
那么什么是異常?更具體地說,C#異常的主要類型是什么,以及如何使用它們?
今天的帖子將回答上述問題以及更多問題。我們將從“異常”的簡要定義開始,然后繼續解釋該結構的組成部分。最后,我們將概述最常見的C#異常以及如何處理它們。
讓我們開始吧。
有什么異常?
異常是一種可用于處理錯誤的機制。就這么簡單。在某些情況下,例如,C程序員將返回錯誤代碼,而Java[2]或C#程序員都很有可能引發異常。
異常表示執行流程的突然中斷。一旦引發異常,執行就會停止。如果未處理異常,則應用程序崩潰。
但是,實際上您是如何做到的呢?您如何引發或捕獲異常?所有這些甚至意味著什么?
這就是我們將在下一部分中詳細介紹的內容。
C#異常剖析
現在,我們將簡要介紹C#異常的情況。您將了解應該使用的主要關鍵字,這些關鍵字不僅可以捕獲和處理異常,還可以拋出自己的異常。我們列表上的第一個是try關鍵字和塊。
try
異常處理解剖的第一部分是try塊。您可以使用它來嘗試執行一些可能引發異常的代碼。考慮以下代碼摘錄:
string content = string.Empty; try {content = System.IO.File.ReadAllText(@"C:\file.txt"); }上面的代碼聲明了一個變量,并為其分配了空字符串。然后,我們有了try塊。塊中的單行代碼try使用該類中的ReadAllText靜態方法System.IO.File。我們擔心該路徑表示的文件可能不存在,在這種情況下會引發異常。但是,當然,這還不夠。我們的代碼不執行任何處理異常的操作。它甚至目前還沒有編譯!這是來自編譯器的消息:
Expected catch or finally該消息是不言自明的。我們需要?catch或finally代碼塊,因為嘗試處理異常然后忘記執行處理部分沒有任何意義。讓我們開始吧。
catch
catch代碼塊使我們能夠實際處理異常。我們將使用更多代碼擴展前面的示例。
看看這個:
static void Main(string[] args){string content = string.Empty;try{Console.WriteLine("First message inside try block.");Console.WriteLine("Second message inside try block.");content = System.IO.File.ReadAllText(@"C:\file.txt");Console.WriteLine("Third message inside try block. This shouldn't be printed.");}catch{Console.WriteLine("First message inside the catch block.");Console.WriteLine("Second message inside the catch block.");}Console.WriteLine("Outside the try-catch.");Console.ReadLine();}如果運行上面的代碼,則應看到以下內容:
First message inside try block. Second message inside try block. First message inside the catch block. Second message inside the catch block. Outside try-catch.該路徑處的文件不存在,因此將引發System.FileNotFoundException。發生這種情況時,執行流程將立即中斷。因此,將在try塊中打印“第三條消息”的行。這不應該被打印。” 永遠不會被執行。
執行流程由catch塊執行。完成后,將控制權交還給main方法,然后打印Outside try-catch。并等待用戶輸入。
最后
在了解了try和之后catch,我們終于(沒有雙關語)準備好使用這個非常有用但經常被誤解的異常處理機制的一部分。那么,什么是finally?
該finally塊是一種確保將執行給定代碼段的方式,無論是否引發異常。考慮下面的代碼:
try {content = System.IO.File.ReadAllText(path);Console.WriteLine("If you're reading this, no exception was thrown."); } catch (System.IO.FileNotFoundException e) {Console.WriteLine("If you're reading this, an exception was thrown.");Console.WriteLine("Message: " + e.Message); } finally {Console.WriteLine("This will be printed, no matter what."); }那仍然是相同的示例,但是現在要簡單得多。注意最后的finally塊。如果運行此代碼,則應該看到以下消息:
If you're reading this, an exception was thrown. Message: Could not find file 'C:\file.txt'. This will be printed, no matter what. Outside the try-catch.現在讓我們模擬文件的存在。注釋掉試圖從文件中讀取的行,然后再次運行該應用程序。這是您現在應該看到的:
If you're reading this, no exception was thrown. This will be printed, no matter what. Outside the try-catch.如您所見,在最近的場景中,沒有引發異常,因此該catch塊中沒有執行任何行。另一方面,finally在兩種情況下都執行了該塊。
throw
當涉及到異常時,您將不會總是從別人那里處理它們。您也可以自己拋出異常。為此,您將使用throw關鍵字,然后是要引發的異常的類的實例化。以下代碼舉例說明了這一點:
public ProductService(IProductRepository repository) {if (repository == null)throw new ArgumentNullException();this.repository = repository; }常見的.NET異常
以下是常見的.NET異常列表:
System.NullReferenceException
這是最著名的(甚至是臭名昭著的)異常之一。當您嘗試調用方法/屬性/索引器/等時,拋出此異常。在包含空引用的變量(即,它不指向任何對象)。下面的代碼將導致空引用異常:
Person p = people.Where(x => x.SSN == verifySsn).FirstOrDefault(); string name = p.Name;在上面的示例中,我們過濾了將每個項目的SSN屬性與verifySsnvariable變量進行比較的序列。然后,我們使用該FirstOrDefault()方法從序列中僅提取第一項。如果序列不產生任何項目,則它將返回該類型的默認值。由于Person是引用類型,因此其返回值為null。
在下一行,我們嘗試Name在空引用上取消引用屬性。Boom!這將空引用異常。
這是通常不拋出也不捕獲的異常。您不要拋出它,因為它毫無意義。如果您想與代碼的調用者交流,給定方法不接受null作為其參數的有效值,則使用的正確異常是System.ArgumentNullException。
您如何“修復”此異常?簡而言之,您必須對可為空性小心謹慎。如果您正在編寫將由第三方使用的任何代碼(即使這些第三方是您的同事),則請認真考慮是否接受空引用作為有效值。
無論您做出什么決定,都必須使該決定非常明確并記錄在案。您可以通過拋出System.ArgumentNullException,例如,并在方法上使用XML文檔標題來實現。
System.IndexOutOfRangeException
在應用程序代碼通常不會拋出或捕獲該異常的意義上,該異常與上一個異常類似。
那為什么呢?
好吧,當您嘗試使用無效的索引值訪問數組,列表或任何可索引序列中的元素時,將引發此異常。一個簡單的例子:
public static void PrintUrlSufix(string url) {var parts = url.Split('.');Console.WriteLine(parts[2]); }上面的代碼顯然希望使用www.acme.com[3]格式的URL 。但是,如果只是獲得acme.com怎么辦?為此,如果得到Hakuna Matata怎么辦?是的,沒錯:System.IndexOutOfBoundException是的。
您如何避免遇到此異常?永遠不要把事情視為理所當然。永遠不要僅僅假設數據將采用正確的格式。做您的盡職調查和檢查的東西。
System.IO.IOException
此C#異常具有一個不言自明的名稱。這正是您的想法:這是IO操作期間發生錯誤時引發的異常。與前兩個異常不同,您可能會發現自己不時捕捉或拋出其中一個。
本IOException類實際上是一些更具體的異常,例如:
?DirectoryNotFoundException?EndOfStreamException?FileNotFoundException?FileLoadException?PathTooLongException
有關的文檔,IOException建議您盡可能使用更具體的異常,而不是更一般的異常。
System.Net.WebException
此異常與網絡有關。如果使用可插拔協議[4]訪問網絡時發生錯誤,則拋出該錯誤。[5]處理此異常時,請記住驗證該Response屬性,該屬性將包含遠程主機返回的響應。
System.Data.SqlClient.SqlException
此異常與數據庫(特別是SQL Server)有關。SQL Server返回錯誤或警告時將引發該錯誤。該類具有一個稱為的屬性Errors,該屬性是一個包含SqlError該類的一個或多個實例的集合。依次包含有關發生的錯誤的詳細信息。
System.StackOverflowException
當執行堆棧溢出時,拋出此異常,這通常意味著遞歸出錯。該代碼有太多的嵌套方法調用。事情就是這樣:這個異常是無法捕獲的-至少從.NET 2.0起就沒有-這意味著當拋出該異常時,您幾乎沒有其他選擇。默認情況下,您的過程將被終止。
您應該做的而不是捕獲此異常的方法是編寫代碼,以防止它首先發生。
System.OutOfMemoryException
可以說這是最令人困惑的C#異常之一[6]。網上有很多資源可以很好地闡明問題[7],但是我將在此處提供一個簡短的版本。發生的情況是此異常不涉及可用的物理內存。
那么,什么時候拋出此異常?
您知道何時要停車嗎,因為其他駕駛員未正確停車而不能停車嗎?如果您只需在停放的汽車之間添加所有可用空間,就足以容納您的車輛。但是目前不可能,因為它不是連續的區域。
當您獲得此內存時,這差不多發生了什么。可能有很多可用的總內存,但是沒有連續的部分可以滿足所需的分配。實際上,例如,如果您嘗試將?StringBuilder的MaxCapacity屬性擴展到該屬性之外,則會發生此異常。
System.InvalidCastException
此異常也具有不言自明的名稱。當代碼由于未定義強制類型轉換而無法從一種類型轉換為另一種類型時,將引發該錯誤。以下代碼將引發此類型的異常:
object o = "10"; int x = (int)o;這是您通常不會捕獲的異常。相反,您將以不會發生的方式編寫代碼。例如,以下代碼在嘗試強制類型轉換之前進行類型檢查:
public override bool Equals (object obj ) {if (!obj is Foo)return false;Foo other = (Foo)obj;return this.bar == other.bar; }上面的代碼可以使用簡化的as操作符[8],在這里我沒有進一步具體說明,作為一個練習留給讀者。無論如何,這就是問題:您實際上應該嘗試做的是避免問題,而不是首先進行轉換。利用泛型來防止陷入需要強制轉換的情況。
System.InvalidOperationException
像之前的許多其他異常一樣,這種異常通常是您不會發現的。相反,您應該做的是編寫不會發生的代碼。考慮以下示例:
var numbers = new List<int> { 1, 3, 5 }; var firstGreaterThanFive = numbers.Where(x => x > 5).First();當序列不產生任何結果時,將拋出First LINQ擴展方法。如果您知道該序列有時可能不會產生結果-沒關系-您應該改用FirstOrDefault。在空序列上調用此方法時,將返回序列類型的默認值,而不是拋出異常。
System.ObjectDisposedException
我們將在這篇文章中介紹的最后一個C#異常也屬于“不應該處理,請修復代碼”類別。換句話說,這是開發人員錯誤。當您嘗試使用已處理的IDisposable[9]進行操作時,將引發此異常。
此當通常發生在開發者調用Dispose,Close或其它類似方法和后來試圖訪問該對象的一個成員時。
如何成功的進行異常處理?
錯誤處理是軟件開發教學中經常被忽略的話題,出現異常有時非常不幸。如果沒有可靠的錯誤處理策略[10],您的應用程序有可能質量會不過關。
通過本文,我們希望通過定義異常的概念并對C#異常的主要類型進行快速概述,以幫助解決該問題。但本文并沒有涵蓋異常處理的全部。恰好相反,我認為這是一個機會,可以開始引導您對該主題的學習,并且永不停止學習和練習。
祝您能想出一種對您的應用程序有效的策略!
References
[1]?異常:?https://raygun.com/blog/java-exceptions-terminology/
[2]?Java:?https://raygun.com/blog/java-exceptions-terminology/
[3]?www.acme.com:?http://www.acme.com
[4]?可插拔協議:?https://docs.microsoft.com/dotnet/framework/network-programming/introducing-pluggable-protocols
[5]?。:?https://docs.microsoft.com/dotnet/framework/network-programming/introducing-pluggable-protocols
[6]?最令人困惑的C#異常之一:?https://stackoverflow.com/questions/1153702/system-outofmemoryexception-was-thrown-when-there-is-still-plenty-of-memory-fr
[7]?可以很好地闡明問題:?https://blogs.msdn.microsoft.com/ericlippert/2009/06/08/out-of-memory-does-not-refer-to-physical-memory/
[8]?as操作符:?https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/as
[9]?IDisposable:?https://docs.microsoft.com/dotnet/api/system.idisposable?view=netframework-4.7.2
[10]?錯誤處理策略:?https://raygun.com/blog/errors-and-exceptions/
總結
以上是生活随笔為你收集整理的常见的C#异常及其修复方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 架构思维其实就那么回事
- 下一篇: .net hbase client--终