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中的现有异常类型,而不是实现自定义异常。 在以下两个条件下,应使用标准异常类型:
引发的异常是由使用错误 (即调用方法) 开发人员在程序逻辑中发生的错误引起的。 通常,会引发异常,例如 ArgumentException、 ArgumentNullException、 InvalidOperationException或 NotSupportedException。 实例化异常对象时提供给异常对象的构造函数的字符串应描述错误,以便开发人员可以修复此错误。 有关更多信息,请参见 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
返回导致异常的非质数。 除了实现受保护的无参数构造函数和具有 SerializationInfo 和 StreamingContext 序列化参数的构造函数外, 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堆栈帧中修改了该异常。