C# 多次抛出同一异常实例

C# 多次抛出同一异常实例,c#,exception-handling,C#,Exception Handling,面对生成一个异常实例然后可能多次抛出它的代码 private readonly Exception exceptionInstance = new Exception("message"); 多次抛出同一异常实例是否正确?否。不建议从代码中的多个位置抛出同一异常实例(不包括重试!) 异常包含的不仅仅是它的消息,它还包含用于调试的有价值的信息,例如堆栈跟踪。 (Update:现在刚刚测试过。堆栈跟踪可能添加到throw语句中的异常中,因此它与此答案无关)和TargetSite(根据我的测试,它似

面对生成一个异常实例然后可能多次抛出它的代码

private readonly Exception exceptionInstance = new Exception("message");

多次抛出同一异常实例是否正确?

否。不建议从代码中的多个位置抛出同一异常实例(不包括重试!)

异常包含的不仅仅是它的消息,它还包含用于调试的有价值的信息,例如堆栈跟踪。 (Update:现在刚刚测试过。堆栈跟踪可能添加到throw语句中的异常中,因此它与此答案无关)和TargetSite(根据我的测试,它似乎是在第一次抛出异常时填充的,但之后再也不会填充)


在代码中的不同位置抛出相同的
异常
实例会使您无法使用某些数据。

由于各种已经说明的原因,这是一种不好的做法,但在多线程代码中失败尤其严重,因为
异常
类(显然)不是线程安全的,这不是一成不变的

考虑以下代码:

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;
    }
}
因此,虽然调用了6次
Throw1()
,调用了4次
Throw2()
,但我们打印的所有10个堆栈跟踪都引用了
Throw1()
方法


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

出于好奇,为什么只想使用特定实例?你的意思是
从代码中抛出exceptionInstance
?异常通常只是一个数据对象。你可以一次又一次地使用同一个实例,但我看不出有任何理由这样做,因为它总是包含完全相同的消息,因此不会有非常通用的用法。Singleton?“但为什么呢?”克劳科德,我不知道作者为什么要这么做。我的问题是关于这段代码可能存在的问题。你当然应该重构这段代码。例如,我很确定这不是线程安全的,所以当你从多个线程抛出这个异常时——谁知道会发生什么呢。堆栈跟踪实际上是不同的。如果您从不同的方法抛出相同的异常,您将在
catch
块中看到堆栈跟踪(以及TargetSite等属性)每次都会更改。但我并不是说这是一种不好的做法…@Evk是的,现在刚刚测试过。堆栈跟踪可能已添加到throw语句中的异常ReadOnly字段不可变。static保证在被调用之前被初始化。只是随便哪个线程初始化静态wins。在那之后,状态永远不会改变。@P.Brian.Mackey我不同意,因为如果您尝试从我的示例中依次调用
Throw1()
Throw2()
catch
将打印两个不同的堆栈跟踪,即使异常相同。异常有许多可变字段,一个例子是
Message
本身,但还有更多。@P.Brian.Mackey注意,问题不在于重新引用捕获到的异常,当然这很好。问题是在一个字段中存储一个异常实例,然后从多个完全不相关的位置抛出它,而不是仅仅“抛出新异常(“测试”)。这是错误的。@P.Brian.Mackey字段初始化在静态构造函数中已经存在。静态字段初始值设定项的行为方式相同。无论如何,问题不在于如何创建
Exception
实例。它只在一个线程上创建一次。问题是它稍后会被多个线程修改。例如,执行
Console.WriteLine(ex)
时,这与
Console.WriteLine(ex.ToString())
相同<异常的code>ToString()调用各种方法,这些方法修改异常的私有字段。这不是线程安全的。@P.Brian.Mackey例如,
ToString()
调用
GetClassName
private方法,该方法大致如下:
if(\u className==null)\u className=GetClassNameIntl();返回_className
。这样,对
ToString()
的无辜调用将修改
Exception
的私有字段。在本例中,我们从多个线程执行此操作,这将导致观察到的行为。所有这些都是因为
Exception
不是线程安全的,也不应该是线程安全的,因为您不应该这样使用它。
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