C#重新抛出范围之外的异常

时间:2011-02-01 13:05:57

标签: c# .net exception

我完全清楚我要问的不是好习惯......但是:

假设我有一个包含函数的类,我希望始终返回一个值,但是存储可能出现的任何异常以供以后处理。喜欢的东西:

public Exception _error { get; set; }

public bool IsValid()
{
    try
    {
        //do something here to cause exception                

        return true;
    }
    catch (Exception ex)
    {
        _error = ex;
        return false;
    }
}

现在我已经存储了异常,是否可以在保持原始堆栈跟踪和异常类型的同时从外部方法抛出异常?

throw _error; //lose stack trace

throw new Exception("", _error) //lose type

感谢您的回答。

修改

感谢一些额外的观点,我意识到以下想法只会带走信息,并没有真正添加或简化情况。再次感谢大家。

<击> 在思考了Pieter的回答和评论之后,我现在想知道如果像下面那样创建一个包装器Exception类可能是一个部分解决方案。这会覆盖尽可能多的异常,以使New异常看起来像它的innerexception,包括stacktrace ..脏我知道,但很有趣:

public class ExceptionWrapper : Exception
{
    private Exception _innerException;

    public ExceptionWrapper(Exception ex) : base("", ex)
    {
        _innerException = ex;
        this.Source = ex.Source;
        this.HelpLink = ex.HelpLink;
    }

    public override string StackTrace
    {
        get
        {
            return _innerException.StackTrace;
        }
    }

    public override System.Collections.IDictionary Data
    {
        get
        {
            return _innerException.Data;
        }
    }

    public override string Message
    {
        get
        {
            return _innerException.Message;
        }
    }

    public new Exception InnerException
    {
        get
        {
            return _innerException.InnerException;
        }
    }
}

<击>

3 个答案:

答案 0 :(得分:7)

不,这是不可能的。

但是,通常通过将异常包装在新的异常中来解决这个问题:

throw new MyException("Wrapper", _error);

这确实维护了_error的堆栈跟踪,但是你确实得到了一个新的异常。第二个例子中的解决方案是处理这些案例的正确方法。

答案 1 :(得分:2)

考虑使用反射来创建正确类型(Activator.CreateInstance)的包装器异常,并调用将接受您存储的内部异常的构造函数。

例如:

[Test]
public void test()
{
    Exception ex = new ArgumentNullException();

    Exception wrapped = (Exception)Activator.
        CreateInstance(ex.GetType(), "wrapped", ex);

    Type expectedType = typeof(ArgumentNullException);

    Assert.IsInstanceOf(expectedType, wrapped, "Is ArgumentNullException.");

    Assert.AreEqual(ex, wrapped.InnerException, "Exception is wrapped.");
}

<强>更新

为了缓解构造函数问题,您可以考虑使用默认构造函数(应该遵循设计指南的异常,但不是必需的),然后通过反射设置其字段来修补新实例。

我同意这种做法高度“ meh ”,这更像是对一个想法的探索。我不推荐它。

异常设计指南需要一个默认的构造函数,所以这种行为可能会在某个框架中继续进行。也许对于某种通信边界的异常序列化\异步反序列化?

答案 2 :(得分:1)

.net-4.5似乎添加了一个新API,用于捕获有关异常的堆栈/信息,并在不同的上下文中重新抛出异常。这称为ExceptionDispatchInfo。如果您发现自己需要间接控制运行任务,例如对作业进行手动线程管理或Task不完全符合您的需求,则非常有用。在您的示例中,它应如下所示:

public ExceptionDispatchInfo _error { get; private set; }

public bool IsValid()
{
    try
    {
        //do something here to cause exception                

        return true;
    }
    catch (Exception ex)
    {
        _error = ExceptionDispatchInfo.Capture(ex);
        return false;
    }
}

/// <summary>Throw underlying exception if invalid.</summary>
public void AssertWasValid() => _error?.Throw();

现在,它不会保留原始调用者。显示的堆栈跟踪显示从原始try块到其中的代码的调用,一个语句打破堆栈的原始和新部分,然后调用ExceptionDispatchInfo.Throw()本身作为新的部分显示的堆栈。这似乎与async代码的跟踪外观类似。如果您关心原始呼叫者,似乎这不起作用。但是如果你关心获得引发异常的行/方法,那就足够了。