多次抛出相同的异常实例

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() 方法。

所以永远不要这样做,因为绝对没有理由这样做。