A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System;
5using System.Reflection;
6using System.Runtime.ExceptionServices;
7
8namespace osu.Framework.Extensions.ExceptionExtensions
9{
10 public static class ExceptionExtensions
11 {
12 /// <summary>
13 /// Rethrows <paramref name="exception"/> as if it was captured in the current context.
14 /// This preserves the stack trace of <paramref name="exception"/>, and will not include the point of rethrow.
15 /// </summary>
16 /// <param name="exception">The captured exception.</param>
17 public static void Rethrow(this Exception exception)
18 {
19 ExceptionDispatchInfo.Capture(exception).Throw();
20 }
21
22 /// <summary>
23 /// Rethrows the <see cref="Exception.InnerException"/> of an <see cref="AggregateException"/> if it exists,
24 /// otherwise, rethrows <paramref name="aggregateException"/>.
25 /// This preserves the stack trace of the exception that is rethrown, and will not include the point of rethrow.
26 /// </summary>
27 /// <param name="aggregateException">The captured exception.</param>
28 public static void RethrowAsSingular(this AggregateException aggregateException) => aggregateException.AsSingular().Rethrow();
29
30 /// <summary>
31 /// Flattens <paramref name="aggregateException"/> into a singular <see cref="Exception"/> if the <paramref name="aggregateException"/>
32 /// contains only a single <see cref="Exception"/>. Otherwise, returns <paramref name="aggregateException"/>.
33 /// </summary>
34 /// <param name="aggregateException">The captured exception.</param>
35 /// <returns>The highest level of flattening possible.</returns>
36 public static Exception AsSingular(this AggregateException aggregateException)
37 {
38 if (aggregateException.InnerExceptions.Count != 1)
39 return aggregateException;
40
41 while (aggregateException.InnerExceptions.Count == 1)
42 {
43 if (!(aggregateException.InnerException is AggregateException innerAggregate))
44 return aggregateException.InnerException;
45
46 aggregateException = innerAggregate;
47 }
48
49 return aggregateException;
50 }
51
52 /// <summary>
53 /// Retrieves the last exception from a recursive <see cref="TargetInvocationException"/>.
54 /// </summary>
55 /// <param name="exception">The exception to retrieve the exception from.</param>
56 /// <returns>The exception at the point of invocation.</returns>
57 public static Exception GetLastInvocation(this TargetInvocationException exception)
58 {
59 var inner = exception.InnerException;
60 while (inner is TargetInvocationException)
61 inner = inner.InnerException;
62 return inner;
63 }
64 }
65}