开发者

C# re-throwing exception outside of scope

I am fully aware that what I am about to ask is not good practice... but:

Let's say I have a class containing a function that I want to always return a value, but store any exceptions that might occur for later processing. Something Like:

public Exception _error { get; set; }

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

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

Now that I have stored the exception, is it at all possible to throw the exception from an outside method while maintaining both the original stack trace and exception type?

throw _error; //lose stack trace

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

Thanks for looking or answering.

EDIT:

Thanks to some additional po开发者_开发百科ints, I realize that the below idea only takes away information and doesn't really add or simplify the situation. Thanks again to everyone.

After pondering Pieter's answer and comments, I'm now wondering if making a wrapper Exception class like the below could be a partial solution. This overrides as much of the exception as possible to make the New exception look like its innerexception, including the stacktrace.. dirty I know, but interesting:

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;
        }
    }
}


No, this is not possible.

However, you normally solve this is by wrapping the exception in a new exception:

throw new MyException("Wrapper", _error);

This does maintain the stack trace of _error, but you do get a new exception. Your solution in your second example is the correct way of handling these cases.


Consider using reflection to create a wrapper exception of the correct type (Activator.CreateInstance) and calling the constructor that will accept the inner exception you have stored.

For example:

[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.");
}

Update

In order to mitigate the constructor issue, you could consider using the default constructor (should be there for an exception that follows design guidelines, but not mandatory) and then patching up the new instance by setting its fields via reflection.

I agree the approach is highly "meh" it's more an exploration of an idea. I wouldn't recommend it.

The exception design guidelines require a default constructor, so this sort of behaviour may go on in the framework somewhere anyway. Perhaps for some sort of icky serialization\deserialization of exceptions across some sort of communications boundary?


It seems that .net-4.5 added a new API for capturing stack/info about exceptions and rethrowing them in different contexts. This is called ExceptionDispatchInfo. It is useful if you find yourself needing more control over running tasks indirectly, like if you do manual thread management for jobs or Task does not exactly fit your needs. In your example, it should look like this:

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();

Now, it doesn’t preserve the original caller. The displayed stack trace shows the calls from the original try block into the code in there, a statement breaking the original and new parts of the stack, and then the calls into ExceptionDispatchInfo.Throw() itself as the new part of the shown stack. This seems similar to how traces with async code look. If you care about the original caller, seems this won’t work. But if you care about getting the line/method that threw the exception, this should be sufficient.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜