public ref class Exception
public ref class Exception : System::Runtime::Serialization::ISerializable
public ref class Exception : System::Runtime::InteropServices::_Exception, System::Runtime::Serialization::ISerializable
public class Exception
public class Exception : System.Runtime.Serialization.ISerializable
[System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.AutoDual)]
[System.Serializable]
public class Exception : System.Runtime.Serialization.ISerializable
[System.Serializable]
[System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.None)]
[System.Runtime.InteropServices.ComVisible(true)]
public class Exception : System.Runtime.InteropServices._Exception, System.Runtime.Serialization.ISerializable
[System.Serializable]
[System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.None)]
[System.Runtime.InteropServices.ComVisible(true)]
public class Exception : System.Runtime.Serialization.ISerializable
type Exception = class
type Exception = class
    interface ISerializable
[<System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.AutoDual)>]
[<System.Serializable>]
type Exception = class
    interface ISerializable
[<System.Serializable>]
[<System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.None)>]
[<System.Runtime.InteropServices.ComVisible(true)>]
type Exception = class
    interface ISerializable
    interface _Exception
[<System.Serializable>]
[<System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.None)>]
[<System.Runtime.InteropServices.ComVisible(true)>]
type Exception = class
    interface ISerializable
Public Class Exception
Public Class Exception
Implements ISerializable
Public Class Exception
Implements _Exception, ISerializable
Object
Exception

以下示例演示 catch F#) with 块中定义的用于处理 ArithmeticException 错误的 (。 此 catch 块还会捕获 DivideByZeroException 错误,因为 DivideByZeroException 派生自 ArithmeticException 并且没有 catch DivideByZeroException 错误显式定义的块。

using namespace System; int main() int x = 0; int y = 100 / x; catch ( ArithmeticException^ e ) Console::WriteLine( "ArithmeticException Handler: {0}", e ); catch ( Exception^ e ) Console::WriteLine( "Generic Exception Handler: {0}", e ); This code example produces the following results: ArithmeticException Handler: System.DivideByZeroException: Attempted to divide by zero. at main() using System; class ExceptionTestClass public static void Main() int x = 0; int y = 100 / x; catch (ArithmeticException e) Console.WriteLine($"ArithmeticException Handler: {e}"); catch (Exception e) Console.WriteLine($"Generic Exception Handler: {e}"); This code example produces the following results: ArithmeticException Handler: System.DivideByZeroException: Attempted to divide by zero. at ExceptionTestClass.Main() module ExceptionTestModule open System let x = 0 let y = 100 / x | :? ArithmeticException as e -> printfn $"ArithmeticException Handler: {e}" | e -> printfn $"Generic Exception Handler: {e}" // This code example produces the following results: // ArithmeticException Handler: System.DivideByZeroException: Attempted to divide by zero. // at <StartupCode$fs>.$ExceptionTestModule.main@() Class ExceptionTestClass Public Shared Sub Main() Dim x As Integer = 0 Dim y As Integer = 100 / x Catch e As ArithmeticException Console.WriteLine("ArithmeticException Handler: {0}", e.ToString()) Catch e As Exception Console.WriteLine("Generic Exception Handler: {0}", e.ToString()) End Try End Sub End Class 'This code example produces the following results: 'ArithmeticException Handler: System.OverflowException: Arithmetic operation resulted in an overflow. ' at ExceptionTestClass.Main()

此类是所有异常的基类。 发生错误时,系统或当前正在执行的应用程序会引发包含错误相关信息的异常来报告错误。 引发异常后,由应用程序或默认异常处理程序处理。

本部分内容:

错误和异常
Try/catch 块
异常类型功能
异常类属性
性能注意事项
重新引发异常
选择标准异常
实现自定义异常

错误和异常

运行时错误可能由于各种原因而发生。 但是,并非所有错误都应作为代码中的异常进行处理。 下面是在运行时可能发生的一些错误类别,以及响应这些错误的适当方法。

  • 使用错误。 使用错误表示程序逻辑中可能导致异常的错误。 但是,错误不应通过异常处理来解决,而应通过修改错误代码来解决。 例如,以下示例中 方法的 Object.Equals(Object) 替代假定 obj 参数必须始终为非 null。

    using System; public class Person private string _name; public string Name get { return _name; } set { _name = value; } public override int GetHashCode() return this.Name.GetHashCode(); public override bool Equals(object obj) // This implementation contains an error in program logic: // It assumes that the obj argument is not null. Person p = (Person) obj; return this.Name.Equals(p.Name); public class Example public static void Main() Person p1 = new Person(); p1.Name = "John"; Person p2 = null; // The following throws a NullReferenceException. Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)); // In F#, null is not a valid state for declared types // without 'AllowNullLiteralAttribute' [<AllowNullLiteral>] type Person() = member val Name = "" with get, set override this.GetHashCode() = this.Name.GetHashCode() override this.Equals(obj) = // This implementation contains an error in program logic: // It assumes that the obj argument is not null. let p = obj :?> Person this.Name.Equals p.Name let p1 = Person() p1.Name <- "John" let p2: Person = null // The following throws a NullReferenceException. printfn $"p1 = p2: {p1.Equals p2}" Public Class Person Private _name As String Public Property Name As String Return _name End Get _name = value End Set End Property Public Overrides Function Equals(obj As Object) As Boolean ' This implementation contains an error in program logic: ' It assumes that the obj argument is not null. Dim p As Person = CType(obj, Person) Return Me.Name.Equals(p.Name) End Function End Class Module Example Public Sub Main() Dim p1 As New Person() p1.Name = "John" Dim p2 As Person = Nothing ' The following throws a NullReferenceException. Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)) End Sub End Module

    NullReferenceException 通过修改源代码以在调用 Object.Equals 替代并重新编译之前显式测试 null,可以消除当 为 null obj 产生的异常。 以下示例包含处理 null 参数的已更正源代码。

    using System; public class Person private string _name; public string Name get { return _name; } set { _name = value; } public override int GetHashCode() return this.Name.GetHashCode(); public override bool Equals(object obj) // This implementation handles a null obj argument. Person p = obj as Person; if (p == null) return false; return this.Name.Equals(p.Name); public class Example public static void Main() Person p1 = new Person(); p1.Name = "John"; Person p2 = null; Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)); // The example displays the following output: // p1 = p2: False // In F#, null is not a valid state for declared types // without 'AllowNullLiteralAttribute' [<AllowNullLiteral>] type Person() = member val Name = "" with get, set override this.GetHashCode() = this.Name.GetHashCode() override this.Equals(obj) = // This implementation handles a null obj argument. match obj with | :? Person as p -> this.Name.Equals p.Name | _ -> false let p1 = Person() p1.Name <- "John" let p2: Person = null printfn $"p1 = p2: {p1.Equals p2}" // The example displays the following output: // p1 = p2: False Public Class Person Private _name As String Public Property Name As String Return _name End Get _name = value End Set End Property Public Overrides Function Equals(obj As Object) As Boolean ' This implementation handles a null obj argument. Dim p As Person = TryCast(obj, Person) If p Is Nothing Then Return False Return Me.Name.Equals(p.Name) End If End Function End Class Module Example Public Sub Main() Dim p1 As New Person() p1.Name = "John" Dim p2 As Person = Nothing Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)) End Sub End Module ' The example displays the following output: ' p1 = p2: False

    可以使用 Debug.Assert 方法识别调试版本中的使用错误,并使用 Trace.Assert 方法识别调试和发布版本中的使用错误,而不是对使用错误使用异常处理。 有关详细信息,请参阅 托管代码中的断言

  • 程序错误。 程序错误是运行时错误,无法通过编写无 bug 代码来避免此错误。

    在某些情况下,程序错误可能会反映预期的或例程错误条件。 在这种情况下,你可能希望避免使用异常处理来处理程序错误,而是重试操作。 例如,如果用户需要输入特定格式的日期,则可以通过调用 DateTime.TryParseExact 方法分析日期字符串,该方法返回一个 Boolean 值,该值指示分析操作是否成功,而不是使用 DateTime.ParseExact 方法,如果日期字符串无法转换为 DateTime 值,该方法将 FormatException 引发异常。 同样,如果用户尝试打开不存在的文件,可以首先调用 File.Exists 方法来检查文件是否存在,如果不存在,则提示用户是否要创建该文件。

    在其他情况下,程序错误反映了可以在代码中处理的意外错误条件。 例如,即使已检查以确保某个文件存在,也可能在打开该文件之前将其删除,或者该文件可能已损坏。 在这种情况下,尝试通过实例化 StreamReader 对象或调用 Open 方法打开文件可能会引发 FileNotFoundException 异常。 在这些情况下,应使用异常处理从错误中恢复。

  • 系统故障。 系统故障是运行时错误,无法以有意义的方式以编程方式进行处理。 例如,如果公共语言运行时无法分配额外的内存,则任何方法都可能会引发 OutOfMemoryException 异常。 通常,系统故障不使用异常处理来处理。 相反,你可能能够使用事件(例如 AppDomain.UnhandledException 并调用 Environment.FailFast 方法)来记录异常信息,并在应用程序终止之前通知用户失败。

    Try/catch 块

    公共语言运行时提供异常处理模型,该模型基于异常作为对象的表示形式,以及将程序代码和异常处理代码分离为 try 块和 catch 块。 可以有一个或多个 catch 块,每个块都设计用于处理特定类型的异常,或者一个块旨在捕获比另一个块更具体的异常。

    如果应用程序处理在执行应用程序代码块期间发生的异常,则必须将代码放置在 语句中 try ,并称为 try 块。 处理块引发 try 的异常的应用程序代码放置在 语句中 catch ,称为 catch 块。 零个或多个 catch 块与一个 try 块相关联,每个 catch 块都包含一个类型筛选器,用于确定它处理的异常类型。

    当块中 try 发生异常时,系统会按它们在应用程序代码中出现的顺序搜索关联的 catch 块,直到找到 catch 处理异常的块。 catch 如果 catch 块的类型筛选器指定 T 或派生自的任何类型, T 则块将处理 类型的 T 异常。 系统在找到处理异常的第一个 catch 块后停止搜索。 出于此原因,在应用程序代码中, catch 必须在处理其基类型的块之前 catch 指定处理类型的块,如本节后面的示例所示。 最后指定处理 System.Exception 的 catch 块。

    如果与当前 try 块关联的块都没有 catch 处理异常,并且当前 try 块嵌套在当前调用中的其他 try 块中, catch 则会搜索与下一个封闭 try 块关联的块。 catch 如果未找到异常的块,则系统会在当前调用中搜索以前的嵌套级别。 如果在当前调用中找不到 catch 异常的块,则会向调用堆栈传递异常,并搜索上一个 catch 堆栈帧来查找处理异常的块。 调用堆栈的搜索将一直持续到异常得到处理或直到调用堆栈上没有更多帧。 如果在未找到 catch 处理异常的块的情况下到达调用堆栈的顶部,则默认异常处理程序会处理该异常,应用程序将终止。

    F# try..with Expression

    F# 不使用 catch 块。 相反,引发的异常是使用单个 with 块匹配的模式。 由于这是一个表达式,而不是语句,因此所有路径必须返回相同的类型。 若要了解详细信息,请参阅 试用...与 表达式

    异常类型功能

    异常类型支持以下功能:

  • 描述错误的可读文本。 发生异常时,运行时会提供一条文本消息,以通知用户错误的性质并建议解决问题的操作。 此文本消息保存在异常对象的 属性中 Message 。 在创建异常对象期间,可以将文本字符串传递给构造函数,以描述该特定异常的详细信息。 如果未向构造函数提供错误消息参数,则使用默认错误消息。 有关更多信息,请参见 Message 属性。

  • 引发异常时调用堆栈的状态。 属性 StackTrace 携带一个堆栈跟踪,该跟踪可用于确定错误在代码中发生的位置。 堆栈跟踪列出所有调用的方法以及进行调用的源文件中的行号。

    异常类属性

    Exception 包含许多属性,这些属性可帮助标识代码位置、类型、帮助文件和异常原因: StackTrace 、、 InnerException Message HelpLink HResult Source TargetSite Data

    当两个或多个异常之间存在因果关系时, InnerException 属性将维护此信息。 引发外部异常以响应此内部异常。 处理外部异常的代码可以使用早期内部异常中的信息来更恰当地处理错误。 有关异常的补充信息可以作为键/值对的集合存储在 属性中 Data

    创建异常对象期间传递给构造函数的错误消息字符串应进行本地化,并且可以使用 类从资源文件 ResourceManager 提供。 有关本地化资源的详细信息,请参阅 创建附属程序集 打包和部署资源 主题。

    若要为用户提供有关异常发生原因的广泛信息, HelpLink 属性可以将 URL (或 URN) 保存到帮助文件。

    Exception 使用 HRESULT COR_E_EXCEPTION,其值0x80131500。

    有关 类实例 Exception 的初始属性值列表,请参阅 Exception 构造函数。

    性能注意事项

    引发或处理异常会消耗大量的系统资源和执行时间。 引发异常只是为了处理真正异常的情况,而不是为了处理可预测的事件或流控制。 例如,在某些情况下,例如在开发类库时,如果方法参数无效,则引发异常是合理的,因为预期使用有效参数调用方法。 无效的方法参数(如果不是使用错误的结果),则表示发生了异常情况。 相反,如果用户输入无效,请不要引发异常,因为用户偶尔会输入无效数据。 请改为提供重试机制,以便用户可以输入有效的输入。 也不应使用异常来处理使用错误。 请改用 断言 来识别和更正使用错误。

    此外,当返回代码足够时,不要引发异常;不要将返回代码转换为异常;和 不定期捕获异常,忽略它,然后继续处理。

    重新引发异常

    在许多情况下,异常处理程序只想将异常传递给调用方。 这种情况最常发生在以下方面:

  • 一个类库,它又包装对.NET Framework类库或其他类库中的方法的调用。

  • 遇到致命异常的应用程序或库。 异常处理程序可以记录异常,然后重新引发异常。

    重新引发异常的建议方法是,只需使用 C# 中的 throw 语句、F# 中的 reraise 函数和 Visual Basic 中的 Throw 语句,而不包括表达式。 这可确保在将异常传播到调用方时保留所有调用堆栈信息。 下面的示例对此进行了演示。 字符串扩展方法 FindOccurrences 包装对 String.IndexOf(String, Int32) 的一个或多个调用,而无需事先验证其参数。

    using System; using System.Collections.Generic; using System.Runtime.CompilerServices; public static class Library public static int[] FindOccurrences(this String s, String f) var indexes = new List<int>(); int currentIndex = 0; try { while (currentIndex >= 0 && currentIndex < s.Length) { currentIndex = s.IndexOf(f, currentIndex); if (currentIndex >= 0) { indexes.Add(currentIndex); currentIndex++; catch (ArgumentNullException e) { // Perform some action here, such as logging this exception. throw; return indexes.ToArray(); open System module Library = let findOccurrences (s: string) (f: string) = let indexes = ResizeArray() let mutable currentIndex = 0 while currentIndex >= 0 && currentIndex < s.Length do currentIndex <- s.IndexOf(f, currentIndex) if currentIndex >= 0 then indexes.Add currentIndex currentIndex <- currentIndex + 1 with :? ArgumentNullException -> // Perform some action here, such as logging this exception. reraise () indexes.ToArray() Imports System.Collections.Generic Imports System.Runtime.CompilerServices Public Module Library <Extension()> Public Function FindOccurrences(s As String, f As String) As Integer() Dim indexes As New List(Of Integer) Dim currentIndex As Integer = 0 Do While currentIndex >= 0 And currentIndex < s.Length currentIndex = s.IndexOf(f, currentIndex) If currentIndex >= 0 Then indexes.Add(currentIndex) currentIndex += 1 End If Catch e As ArgumentNullException ' Perform some action here, such as logging this exception. Throw End Try Return indexes.ToArray() End Function End Module

    调用方随后调用 FindOccurrences 两次。 在对 的第二次 null 调用 FindOccurrences 中,调用方传递 作为搜索字符串,这会导致 String.IndexOf(String, Int32) 方法引发 ArgumentNullException 异常。 此异常由 FindOccurrences 方法处理,并传递回调用方。 由于 throw 语句的使用没有表达式,因此示例中的输出显示调用堆栈已保留。

    public class Example public static void Main() String s = "It was a cold day when..."; int[] indexes = s.FindOccurrences("a"); ShowOccurrences(s, "a", indexes); Console.WriteLine(); String toFind = null; try { indexes = s.FindOccurrences(toFind); ShowOccurrences(s, toFind, indexes); catch (ArgumentNullException e) { Console.WriteLine("An exception ({0}) occurred.", e.GetType().Name); Console.WriteLine("Message:\n {0}\n", e.Message); Console.WriteLine("Stack Trace:\n {0}\n", e.StackTrace); private static void ShowOccurrences(String s, String toFind, int[] indexes) Console.Write("'{0}' occurs at the following character positions: ", toFind); for (int ctr = 0; ctr < indexes.Length; ctr++) Console.Write("{0}{1}", indexes[ctr], ctr == indexes.Length - 1 ? "" : ", "); Console.WriteLine(); // The example displays the following output: // 'a' occurs at the following character positions: 4, 7, 15 // An exception (ArgumentNullException) occurred. // Message: // Value cannot be null. // Parameter name: value // Stack Trace: // at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri // ngComparison comparisonType) // at Library.FindOccurrences(String s, String f) // at Example.Main() open Library let showOccurrences toFind (indexes: int[]) = printf $"'{toFind}' occurs at the following character positions: " for i = 0 to indexes.Length - 1 do printf $"""{indexes[i]}{if i = indexes.Length - 1 then "" else ", "}""" printfn "" let s = "It was a cold day when..." let indexes = findOccurrences s "a" showOccurrences "a" indexes printfn "" let toFind: string = null let indexes = findOccurrences s toFind showOccurrences toFind indexes with :? ArgumentNullException as e -> printfn $"An exception ({e.GetType().Name}) occurred." printfn $"Message:\n {e.Message}\n" printfn $"Stack Trace:\n {e.StackTrace}\n" // The example displays the following output: // 'a' occurs at the following character positions: 4, 7, 15 // An exception (ArgumentNullException) occurred. // Message: // Value cannot be null. (Parameter 'value') // Stack Trace: // at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri // ngComparison comparisonType) // at Library.findOccurrences(String s, String f) // at <StartupCode$fs>.main@() Module Example Public Sub Main() Dim s As String = "It was a cold day when..." Dim indexes() As Integer = s.FindOccurrences("a") ShowOccurrences(s, "a", indexes) Console.WriteLine() Dim toFind As String = Nothing indexes = s.FindOccurrences(toFind) ShowOccurrences(s, toFind, indexes) Catch e As ArgumentNullException Console.WriteLine("An exception ({0}) occurred.", e.GetType().Name) Console.WriteLine("Message:{0} {1}{0}", vbCrLf, e.Message) Console.WriteLine("Stack Trace:{0} {1}{0}", vbCrLf, e.StackTrace) End Try End Sub Private Sub ShowOccurrences(s As String, toFind As String, indexes As Integer()) Console.Write("'{0}' occurs at the following character positions: ", toFind) For ctr As Integer = 0 To indexes.Length - 1 Console.Write("{0}{1}", indexes(ctr), If(ctr = indexes.Length - 1, "", ", ")) Console.WriteLine() End Sub End Module ' The example displays the following output: ' 'a' occurs at the following character positions: 4, 7, 15 ' An exception (ArgumentNullException) occurred. ' Message: ' Value cannot be null. ' Parameter name: value ' Stack Trace: ' at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri ' ngComparison comparisonType) ' at Library.FindOccurrences(String s, String f) ' at Example.Main()

    相比之下,如果使用 重新引发异常

    throw e;
    
    Throw e  
    
    raise e
    

    语句中,不会保留完整的调用堆栈,该示例将生成以下输出:

    'a' occurs at the following character positions: 4, 7, 15  
    An exception (ArgumentNullException) occurred.  
    Message:  
       Value cannot be null.  
    Parameter name: value  
    Stack Trace:  
          at Library.FindOccurrences(String s, String f)  
       at Example.Main()  
    

    稍微麻烦一些的替代方法是引发新的异常,并在内部异常中保留原始异常的调用堆栈信息。 然后,调用方可以使用新异常的 InnerException 属性来检索堆栈帧和有关原始异常的其他信息。 在本例中,throw 语句为:

    throw new ArgumentNullException("You must supply a search string.", raise (ArgumentNullException("You must supply a search string.", e) ) Throw New ArgumentNullException("You must supply a search string.",

    处理异常的用户代码必须知道 InnerException 属性包含有关原始异常的信息,如以下异常处理程序所示。

    try { indexes = s.FindOccurrences(toFind); ShowOccurrences(s, toFind, indexes); catch (ArgumentNullException e) { Console.WriteLine("An exception ({0}) occurred.", e.GetType().Name); Console.WriteLine(" Message:\n{0}", e.Message); Console.WriteLine(" Stack Trace:\n {0}", e.StackTrace); Exception ie = e.InnerException; if (ie != null) { Console.WriteLine(" The Inner Exception:"); Console.WriteLine(" Exception Name: {0}", ie.GetType().Name); Console.WriteLine(" Message: {0}\n", ie.Message); Console.WriteLine(" Stack Trace:\n {0}\n", ie.StackTrace); // The example displays the following output: // 'a' occurs at the following character positions: 4, 7, 15 // An exception (ArgumentNullException) occurred. // Message: You must supply a search string. // Stack Trace: // at Library.FindOccurrences(String s, String f) // at Example.Main() // The Inner Exception: // Exception Name: ArgumentNullException // Message: Value cannot be null. // Parameter name: value // Stack Trace: // at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri // ngComparison comparisonType) // at Library.FindOccurrences(String s, String f) let indexes = findOccurrences s toFind showOccurrences toFind indexes with :? ArgumentNullException as e -> printfn $"An exception ({e.GetType().Name}) occurred." printfn $" Message:\n{e.Message}" printfn $" Stack Trace:\n {e.StackTrace}" let ie = e.InnerException if ie <> null then printfn " The Inner Exception:" printfn $" Exception Name: {ie.GetType().Name}" printfn $" Message: {ie.Message}\n" printfn $" Stack Trace:\n {ie.StackTrace}\n" // The example displays the following output: // 'a' occurs at the following character positions: 4, 7, 15 // An exception (ArgumentNullException) occurred. // Message: You must supply a search string. // Stack Trace: // at Library.FindOccurrences(String s, String f) // at Example.Main() // The Inner Exception: // Exception Name: ArgumentNullException // Message: Value cannot be null. // Parameter name: value // Stack Trace: // at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri // ngComparison comparisonType) // at Library.FindOccurrences(String s, String f) indexes = s.FindOccurrences(toFind) ShowOccurrences(s, toFind, indexes) Catch e As ArgumentNullException Console.WriteLine("An exception ({0}) occurred.", e.GetType().Name) Console.WriteLine(" Message: {1}{0}", vbCrLf, e.Message) Console.WriteLine(" Stack Trace:{0} {1}{0}", vbCrLf, e.StackTrace) Dim ie As Exception = e.InnerException If ie IsNot Nothing Then Console.WriteLine(" The Inner Exception:") Console.WriteLine(" Exception Name: {0}", ie.GetType().Name) Console.WriteLine(" Message: {1}{0}", vbCrLf, ie.Message) Console.WriteLine(" Stack Trace:{0} {1}{0}", vbCrLf, ie.StackTrace) End If End Try ' The example displays the following output: ' 'a' occurs at the following character positions: 4, 7, 15 ' An exception (ArgumentNullException) occurred. ' Message: You must supply a search string. ' Stack Trace: ' at Library.FindOccurrences(String s, String f) ' at Example.Main() ' The Inner Exception: ' Exception Name: ArgumentNullException ' Message: Value cannot be null. ' Parameter name: value ' Stack Trace: ' at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri ' ngComparison comparisonType) ' at Library.FindOccurrences(String s, String f)

    选择标准异常

    当必须引发异常时,通常可以使用.NET Framework中的现有异常类型,而不是实现自定义异常。 在以下两个条件下,应使用标准异常类型:

  • 引发的异常是由使用错误 (即调用方法) 开发人员在程序逻辑中发生的错误引起的。 通常,会引发异常,例如 ArgumentExceptionArgumentNullExceptionInvalidOperationExceptionNotSupportedException。 实例化异常对象时提供给异常对象的构造函数的字符串应描述错误,以便开发人员可以修复此错误。 有关更多信息,请参见 Message 属性。

  • 你正在处理一个错误,该错误可以通过现有的.NET Framework异常传达给调用方。 应可能引发派生最多的异常。 例如,如果方法要求参数是枚举类型的有效成员,则应引发 InvalidEnumArgumentException (派生最多的类) 而不是 ArgumentException

    下表列出了常见的异常类型以及引发它们的条件。

  • 当异常反映无法映射到现有的唯一程序错误时,.NET Framework异常。

  • 当异常需要与适用于现有.NET Framework异常的处理不同的处理时,或者必须消除异常与类似异常的歧义。 例如,如果在分析不在目标整型类型的范围内的字符串的数值表示形式时引发 ArgumentOutOfRangeException 异常,则不希望对调用方在调用 方法时未提供适当约束值而引发的错误使用相同的异常。

    Exception是.NET Framework中所有异常的基类。 许多派生类依赖于类成员的 Exception 继承行为;它们不重写 的成员 Exception,也不定义任何唯一成员。

    定义自己的异常类:

  • 定义继承自 Exception的类。 如有必要,请定义类所需的任何唯一成员,以提供有关异常的其他信息。 例如, ArgumentException 类包含一个 ParamName 属性,该属性指定其参数导致异常的参数的名称,该 RegexMatchTimeoutException 属性包含指示 MatchTimeout 超时间隔的属性。

  • 如有必要,请重写要更改或修改其功能的任何继承成员。 请注意,的大多数现有派生类 Exception 不会替代继承成员的行为。

  • 确定自定义异常对象是否可序列化。 序列化使你能够保存有关异常的信息,并允许服务器和客户端代理在远程处理上下文中共享异常信息。 若要使异常对象可序列化,请使用 属性标记它 SerializableAttribute

  • 定义异常类的构造函数。 通常,异常类具有以下一个或多个构造函数:

  • Exception(),它使用默认值初始化新异常对象的属性。

  • Exception(String),它用指定的错误消息初始化新的异常对象。

  • Exception(String, Exception),它初始化具有指定错误消息和内部异常的新异常对象。

  • Exception(SerializationInfo, StreamingContext),它是从 protected 序列化数据初始化新异常对象的构造函数。 如果选择使异常对象可序列化,则应实现此构造函数。

    以下示例演示了自定义异常类的用法。 它定义当 NotPrimeException 客户端尝试通过指定非质数的起始数来检索质数序列时引发的异常。 异常定义一个新的属性 ,该属性 NonPrime返回导致异常的非质数。 除了实现受保护的无参数构造函数和具有 SerializationInfoStreamingContext 序列化参数的构造函数外, NotPrimeException 类还定义了三个附加构造函数以支持 NonPrime 属性。 每个构造函数除了保留非质数的值外,还调用基类构造函数。 类 NotPrimeException 还标有 SerializableAttribute 特性。

    using System; using System.Runtime.Serialization; [Serializable()] public class NotPrimeException : Exception private int notAPrime; protected NotPrimeException() : base() public NotPrimeException(int value) : base(String.Format("{0} is not a prime number.", value)) notAPrime = value; public NotPrimeException(int value, string message) : base(message) notAPrime = value; public NotPrimeException(int value, string message, Exception innerException) : base(message, innerException) notAPrime = value; protected NotPrimeException(SerializationInfo info, StreamingContext context) : base(info, context) public int NonPrime { get { return notAPrime; } } namespace global open System open System.Runtime.Serialization [<Serializable>] type NotPrimeException = inherit Exception val notAPrime: int member this.NonPrime = this.notAPrime new (value) = { inherit Exception($"%i{value} is not a prime number."); notAPrime = value } new (value, message) = { inherit Exception(message); notAPrime = value } new (value, message, innerException: Exception) = { inherit Exception(message, innerException); notAPrime = value } // F# does not support protected members new () = { inherit Exception(); notAPrime = 0 } new (info: SerializationInfo, context: StreamingContext) = { inherit Exception(info, context); notAPrime = 0 } Imports System.Runtime.Serialization <Serializable()> _ Public Class NotPrimeException : Inherits Exception Private notAPrime As Integer Protected Sub New() MyBase.New() End Sub Public Sub New(value As Integer) MyBase.New(String.Format("{0} is not a prime number.", value)) notAPrime = value End Sub Public Sub New(value As Integer, message As String) MyBase.New(message) notAPrime = value End Sub Public Sub New(value As Integer, message As String, innerException As Exception) MyBase.New(message, innerException) notAPrime = value End Sub Protected Sub New(info As SerializationInfo, context As StreamingContext) MyBase.New(info, context) End Sub Public ReadOnly Property NonPrime As Integer Return notAPrime End Get End Property End Class

    PrimeNumberGenerator以下示例中显示的 类使用 Eratosthenes 的 Sieve 计算从 2 到客户端在调用其类构造函数中指定的限制的质数序列。 方法 GetPrimesFrom 返回大于或等于指定下限的所有质数,但如果下限不是质数,则引发 NotPrimeException

    using System; using System.Collections.Generic; [Serializable] public class PrimeNumberGenerator private const int START = 2; private int maxUpperBound = 10000000; private int upperBound; private bool[] primeTable; private List<int> primes = new List<int>(); public PrimeNumberGenerator(int upperBound) if (upperBound > maxUpperBound) string message = String.Format( "{0} exceeds the maximum upper bound of {1}.", upperBound, maxUpperBound); throw new ArgumentOutOfRangeException(message); this.upperBound = upperBound; // Create array and mark 0, 1 as not prime (True). primeTable = new bool[upperBound + 1]; primeTable[0] = true; primeTable[1] = true; // Use Sieve of Eratosthenes to determine prime numbers. for (int ctr = START; ctr <= (int)Math.Ceiling(Math.Sqrt(upperBound)); ctr++) if (primeTable[ctr]) continue; for (int multiplier = ctr; multiplier <= upperBound / ctr; multiplier++) if (ctr * multiplier <= upperBound) primeTable[ctr * multiplier] = true; // Populate array with prime number information. int index = START; while (index != -1) index = Array.FindIndex(primeTable, index, (flag) => !flag); if (index >= 1) primes.Add(index); index++; public int[] GetAllPrimes() return primes.ToArray(); public int[] GetPrimesFrom(int prime) int start = primes.FindIndex((value) => value == prime); if (start < 0) throw new NotPrimeException(prime, String.Format("{0} is not a prime number.", prime)); return primes.FindAll((value) => value >= prime).ToArray(); namespace global open System [<Serializable>] type PrimeNumberGenerator(upperBound) = let start = 2 let maxUpperBound = 10000000 let primes = ResizeArray() let primeTable = upperBound + 1 |> Array.zeroCreate<bool> if upperBound > maxUpperBound then let message = $"{upperBound} exceeds the maximum upper bound of {maxUpperBound}." raise (ArgumentOutOfRangeException message) // Create array and mark 0, 1 as not prime (True). primeTable[0] <- true primeTable[1] <- true // Use Sieve of Eratosthenes to determine prime numbers. for i = start to float upperBound |> sqrt |> ceil |> int do if not primeTable[i] then for multiplier = i to upperBound / i do if i * multiplier <= upperBound then primeTable[i * multiplier] <- true // Populate array with prime number information. let mutable index = start while index <> -1 do index <- Array.FindIndex(primeTable, index, fun flag -> not flag) if index >= 1 then primes.Add index index <- index + 1 member _.GetAllPrimes() = primes.ToArray() member _.GetPrimesFrom(prime) = let start = Seq.findIndex ((=) prime) primes if start < 0 then raise (NotPrimeException(prime, $"{prime} is not a prime number.") ) Seq.filter ((>=) prime) primes |> Seq.toArray Imports System.Collections.Generic <Serializable()> Public Class PrimeNumberGenerator Private Const START As Integer = 2 Private maxUpperBound As Integer = 10000000 Private upperBound As Integer Private primeTable() As Boolean Private primes As New List(Of Integer) Public Sub New(upperBound As Integer) If upperBound > maxUpperBound Then Dim message As String = String.Format( "{0} exceeds the maximum upper bound of {1}.", upperBound, maxUpperBound) Throw New ArgumentOutOfRangeException(message) End If Me.upperBound = upperBound ' Create array and mark 0, 1 as not prime (True). ReDim primeTable(upperBound) primeTable(0) = True primeTable(1) = True ' Use Sieve of Eratosthenes to determine prime numbers. For ctr As Integer = START To CInt(Math.Ceiling(Math.Sqrt(upperBound))) If primeTable(ctr) Then Continue For For multiplier As Integer = ctr To CInt(upperBound \ ctr) If ctr * multiplier <= upperBound Then primeTable(ctr * multiplier) = True ' Populate array with prime number information. Dim index As Integer = START Do While index <> -1 index = Array.FindIndex(primeTable, index, Function(flag) Return Not flag End Function) If index >= 1 Then primes.Add(index) index += 1 End If End Sub Public Function GetAllPrimes() As Integer() Return primes.ToArray() End Function Public Function GetPrimesFrom(prime As Integer) As Integer() Dim start As Integer = primes.FindIndex(Function(value) Return value = prime End Function) If start < 0 Then Throw New NotPrimeException(prime, String.Format("{0} is not a prime number.", prime)) Return primes.FindAll(Function(value) Return value >= prime End Function).ToArray() End If End Function End Class

    以下示例使用非质数对 GetPrimesFrom 方法进行两次调用,其中一个调用跨越应用程序域边界。 在这两种情况下,异常都引发并在客户端代码中成功处理。

    using System; using System.Reflection; class Example public static void Main() int limit = 10000000; PrimeNumberGenerator primes = new PrimeNumberGenerator(limit); int start = 1000001; int[] values = primes.GetPrimesFrom(start); Console.WriteLine("There are {0} prime numbers from {1} to {2}", start, limit); catch (NotPrimeException e) Console.WriteLine("{0} is not prime", e.NonPrime); Console.WriteLine(e); Console.WriteLine("--------"); AppDomain domain = AppDomain.CreateDomain("Domain2"); PrimeNumberGenerator gen = (PrimeNumberGenerator)domain.CreateInstanceAndUnwrap( typeof(Example).Assembly.FullName, "PrimeNumberGenerator", true, BindingFlags.Default, null, new object[] { 1000000 }, null, null); start = 100; Console.WriteLine(gen.GetPrimesFrom(start)); catch (NotPrimeException e) Console.WriteLine("{0} is not prime", e.NonPrime); Console.WriteLine(e); Console.WriteLine("--------"); open System open System.Reflection let limit = 10000000 let primes = PrimeNumberGenerator limit let start = 1000001 let values = primes.GetPrimesFrom start printfn $"There are {values.Length} prime numbers from {start} to {limit}" with :? NotPrimeException as e -> printfn $"{e.NonPrime} is not prime" printfn $"{e}" printfn "--------" let domain = AppDomain.CreateDomain "Domain2" let gen = domain.CreateInstanceAndUnwrap( typeof<PrimeNumberGenerator>.Assembly.FullName, "PrimeNumberGenerator", true, BindingFlags.Default, null, [| box 1000000 |], null, null) :?> PrimeNumberGenerator let start = 100 printfn $"{gen.GetPrimesFrom start}" with :? NotPrimeException as e -> printfn $"{e.NonPrime} is not prime" printfn $"{e}" printfn "--------" Imports System.Reflection Module Example Sub Main() Dim limit As Integer = 10000000 Dim primes As New PrimeNumberGenerator(limit) Dim start As Integer = 1000001 Dim values() As Integer = primes.GetPrimesFrom(start) Console.WriteLine("There are {0} prime numbers from {1} to {2}", start, limit) Catch e As NotPrimeException Console.WriteLine("{0} is not prime", e.NonPrime) Console.WriteLine(e) Console.WriteLine("--------") End Try Dim domain As AppDomain = AppDomain.CreateDomain("Domain2") Dim gen As PrimeNumberGenerator = domain.CreateInstanceAndUnwrap( GetType(Example).Assembly.FullName, "PrimeNumberGenerator", True, BindingFlags.Default, Nothing, {1000000}, Nothing, Nothing) start = 100 Console.WriteLine(gen.GetPrimesFrom(start)) Catch e As NotPrimeException Console.WriteLine("{0} is not prime", e.NonPrime) Console.WriteLine(e) Console.WriteLine("--------") End Try End Sub End Module ' The example displays the following output: ' 1000001 is not prime ' NotPrimeException: 1000001 is not a prime number. ' at PrimeNumberGenerator.GetPrimesFrom(Int32 prime) ' at Example.Main() ' -------- ' 100 is not prime ' NotPrimeException: 100 is not a prime number. ' at PrimeNumberGenerator.GetPrimesFrom(Int32 prime) ' at Example.Main() ' --------

    Windows 运行时 和 .NET Framework 4.5.1

    在 .NET for Windows 8.x Store for Windows 8 应用中,当异常通过非.NET Framework堆栈帧传播时,通常会丢失一些异常信息。 从 .NET Framework 4.5.1 和 Windows 8.1 开始,公共语言运行时将继续使用引发的原始Exception对象,除非在非.NET Framework堆栈帧中修改了该异常。

  •