在finally块中抛出异常

10

有没有一种方法可以获取当前抛出的异常(如果存在)?

我想要减少代码量并对类似以下任务应用一些重用:

Exception thrownException = null;
try {
    // some code with 3rd party classes, which can throw unexpected exceptions
}
catch( Exception exc ) {
    thrownException = exc;
    LogException( exc );
}
finally {
    if ( null == thrownException ) {
        // some code
    }
    else {
        // some code
    }
}

将其替换为以下代码:

using( ExceptionHelper.LogException() ) {
    // some code with 3rd party classes, which can throw unexpected exceptions
}
using( new ExceptionHelper { ExceptionAction = ()=> /*some cleaning code*/ } ) {
    // some code with 3rd party classes, which can throw unexpected exceptions
}

public class ExceptiohHelper : IDisposable {
    public static ExceptionHelper LogException() {
        return new ExceptionHelper();
    }

    public Action SuccessfulAction {get; set;}
    public Action ExceptionAction {get; set;}

    public void Dispose() {
        Action action;
        Exception thrownException = TheMethodIDontKnow();
        if ( null != thrownException ) {
            LogException( thrownException );
            action = this.ExceptionAction;
        }
        else {
            action = this.SuccessfulAction;
        }

        if ( null != action ) {
            action();
        }
    }
}

这种情况可能吗?
谢谢
4个回答

10

这个想法是在catch块中处理异常...

尽管如此,Exception是一个引用类型,因此您始终可以在try作用域之外声明一个Exception变量...

Exception dontDoThis;
try
{
    foo.DoSomething();
}
catch(Exception e)
{
    dontDoThis = e;
}
finally
{
    // use dontDoThis...
}

3
是的,我知道try-catch-finally的用法。正如我在问题中所说的那样,我现在正在使用它 - 请再读一遍。但我想要减少编写代码的量,并扩展重用性。这绝不是任何答案 :( - TcKs

5

你对以下内容有何看法。不要将问题视为“如何获取最后一个异常?”,而是将其改为“如何使用更多控制运行一些代码?”

例如: 你可以使用ActionRunner代替ExceptionHelper。

public class ActionRunner
{
    public Action AttemptAction { get; set; }
    public Action SuccessfulAction { get; set; }
    public Action ExceptionAction { get; set; }

    public void RunAction()
    {
        try
        {
            AttemptAction();
            SuccessfulAction();
        }
        catch (Exception ex)
        {
            LogException(ex);
            ExceptionAction();
        }
    }

    private void LogException(Exception thrownException) { /* log here... */ }
}

假设只有AttemptAction在调用之间发生变化,那么这至少可以让你重复使用SuccessfulAction和ExceptionAction。

var actionRunner = new ActionRunner
{
    AttemptAction = () =>
    {
        Console.WriteLine("Going to throw...");
        throw new Exception("Just throwing");
    },
    ExceptionAction = () => Console.WriteLine("ExceptionAction"),
    SuccessfulAction = () => Console.WriteLine("SuccessfulAction"),
};
actionRunner.RunAction();

actionRunner.AttemptAction = () => Console.WriteLine("Running some other code...");
actionRunner.RunAction();

是的!“模板方法”设计模式是解决方案。谢谢! - TcKs

3

如果您想捕获意外的异常,您应该处理UnhandledException。您只应该在您打算处理(而不仅仅是记录)的较低级别处捕获异常,否则您应该让它们上升并在更高级别或如我之前提到的UnhandledException方法中被捕获。


UnhandlerException是一个全局事件,我丢失了所有上下文信息 - 它对不同代码中的具体异常处理毫无用处。然而,这比@Mark Seemann提供的更好的答案。也许有一些方法,可以动态地附加/分离UnhandledException。我会尝试一下。 - TcKs
UnhandledException看起来不错,但如果异常在调用者代码中某处被处理,那么该事件就不会被触发(这很合理——异常已被处理)。但还是谢谢,也许我可以找到一些"HandledException"事件。 - TcKs
如果您仍然希望在UnhandledException中处理异常,但想要在内部处理它,只需处理它,然后重新引发它。 - James
我想跳过try-catch-finally块,因为没有有效的方法可以在大型项目中重用它,并且管理重构非常耗时。只有在堆栈跟踪中没有catch块时,才会引发UnhandledException。因此,如果您有“void Main(){try {new Form1()。ShowDialog();} catch {...} }”,则永远不会触发UnhandledException事件。 - TcKs
@Tcks...不是这样的。如果您没有处理引发的异常,将会触发UnhandledException事件。当然,除非您捕获了Exception(基类),但我认为这是不好的设计。 - James

1
这是一个非常古老的问题,但如今有一些可能性。 这个 GitHub 问题包含了一个实现方法的示例:
async Task Main()
{
    using var activity1 = new Activity();
    try
    {
        using var activity2 = new Activity();
        await ThrowAsync();
    }
    catch
    {       
    }
}

public Task ThrowAsync() => Task.Run(() => throw new OperationCanceledException());

public sealed class Activity : IDisposable
{
    [ThreadStatic]
    private static GCHandle _currentExceptionHandle;
    
    static Activity()
    {
        AppDomain.CurrentDomain.FirstChanceException += (s,e) =>
        {
            if (_currentExceptionHandle.IsAllocated)
            {
                _currentExceptionHandle.Free();
            }
            _currentExceptionHandle = GCHandle.Alloc(e.Exception, GCHandleType.Weak);
        };
    }
    
    private readonly IntPtr _exceptionAddress;

    public Activity()
    {
        _exceptionAddress = Marshal.GetExceptionPointers();
    }

    public void Dispose()
    {
        var curentExceptionAddr = Marshal.GetExceptionPointers();
        var isExceptionBubblingUp = _exceptionAddress != curentExceptionAddr && curentExceptionAddr != IntPtr.Zero;
        if (isExceptionBubblingUp && _currentExceptionHandle.Target is Exception currentException)
        {
            Console.WriteLine($"Thrown exception {currentException}");
        }
        else if (_currentExceptionHandle.IsAllocated)
        {
            _currentExceptionHandle.Free();
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接