多次抛出相同的异常实例
Throw same exception instance multiple times
遇到生成一个异常实例然后可能多次抛出它的代码。
private readonly Exception exceptionInstance = new Exception("message");
多次抛出同一个异常实例是否正确?
没有。不建议从代码中的多个位置抛出 Exception
的相同实例(不计算重新抛出!)。
异常包含的不仅仅是它的消息 - 它包含对调试有用的信息,例如堆栈跟踪。
(Update:现在刚刚测试过。堆栈跟踪可能已添加到 throw 语句中的异常,因此它与此答案无关)和 TargetSite(从我的测试来看,似乎是第一次抛出异常时被填充,但之后再也不会被填充)。
在代码的不同位置使用 Exception
的同一个实例会使您无法使用其中的某些数据。
出于各种已经说明的原因,这是一种不好的做法,但它在多线程代码中尤其会失败,因为 Exception
class(显然)不是线程安全的,而且它不是不可变的。
考虑这段代码:
class Program {
static readonly Exception _test = new Exception("test");
static void Main(string[] args) {
ThreadPool.SetMinThreads(10, 8);
var random = new Random();
int num1 = 0;
int num2 = 0;
var tasks = new List<Task>();
for (int i = 0; i < 10; i++) {
tasks.Add(Task.Run(() => {
try {
if (random.Next(0, 2) == 0) {
Interlocked.Increment(ref num1);
Throw1();
}
else {
Interlocked.Increment(ref num2);
Throw2();
}
}
catch (Exception ex) {
Console.WriteLine(ex);
}
}));
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine("num1: " + num1);
Console.WriteLine("num2: " + num2);
Console.ReadKey();
}
static void Throw1() {
throw _test;
}
static void Throw2() {
throw _test;
}
}
这里我们有 2 个方法,Throw1()
和 Throw2()
,它们都从私有字段中抛出相同的异常实例。然后我们 运行 10 个线程随机调用 Throw1()
或 Throw2()
并打印抛出的内容。此类代码的示例输出为:
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
num1: 6
num2: 4
所以虽然 Throw1()
被调用了 6 次并且 Throw2()
被调用了 4 次 - 我们打印的所有 10 个堆栈跟踪都引用了 Throw1()
方法。
所以永远不要这样做,因为绝对没有理由这样做。
遇到生成一个异常实例然后可能多次抛出它的代码。
private readonly Exception exceptionInstance = new Exception("message");
多次抛出同一个异常实例是否正确?
没有。不建议从代码中的多个位置抛出 Exception
的相同实例(不计算重新抛出!)。
异常包含的不仅仅是它的消息 - 它包含对调试有用的信息,例如堆栈跟踪。 (Update:现在刚刚测试过。堆栈跟踪可能已添加到 throw 语句中的异常,因此它与此答案无关)和 TargetSite(从我的测试来看,似乎是第一次抛出异常时被填充,但之后再也不会被填充)。
在代码的不同位置使用 Exception
的同一个实例会使您无法使用其中的某些数据。
出于各种已经说明的原因,这是一种不好的做法,但它在多线程代码中尤其会失败,因为 Exception
class(显然)不是线程安全的,而且它不是不可变的。
考虑这段代码:
class Program {
static readonly Exception _test = new Exception("test");
static void Main(string[] args) {
ThreadPool.SetMinThreads(10, 8);
var random = new Random();
int num1 = 0;
int num2 = 0;
var tasks = new List<Task>();
for (int i = 0; i < 10; i++) {
tasks.Add(Task.Run(() => {
try {
if (random.Next(0, 2) == 0) {
Interlocked.Increment(ref num1);
Throw1();
}
else {
Interlocked.Increment(ref num2);
Throw2();
}
}
catch (Exception ex) {
Console.WriteLine(ex);
}
}));
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine("num1: " + num1);
Console.WriteLine("num2: " + num2);
Console.ReadKey();
}
static void Throw1() {
throw _test;
}
static void Throw2() {
throw _test;
}
}
这里我们有 2 个方法,Throw1()
和 Throw2()
,它们都从私有字段中抛出相同的异常实例。然后我们 运行 10 个线程随机调用 Throw1()
或 Throw2()
并打印抛出的内容。此类代码的示例输出为:
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
num1: 6
num2: 4
所以虽然 Throw1()
被调用了 6 次并且 Throw2()
被调用了 4 次 - 我们打印的所有 10 个堆栈跟踪都引用了 Throw1()
方法。
所以永远不要这样做,因为绝对没有理由这样做。