Ayende @ Rahien

Unnatural acts on source code

Did you know: Find out if an exception was thrown from a finally block!

This is a big biggie for me, because it enables a much nicer syntax for a lot of stuff. But first, let us show this:

using(new ExceptionDetector())
{
	if(new Random().Next(1,10)%2 == 0)
          throw new Exception();
}

How can you tell, from the ExceptionDetector, if an exception was thrown or not? Well, conventional wisdom, and what I thought about until 15 minutes ago, says that you can't. I want to thank Daniel Fortunov, for teaching me this trick:

public class ExceptionDetector : IDisposable
{
    public void Dispose()
    {
        if (Marshal.GetExceptionCode()==0)
            Console.WriteLine("Completed Successfully!");
        else
            Console.WriteLine("Exception!");
    }
}
Amazing!

Comments

inoodle
06/19/2007 11:32 PM by
inoodle

Ooooo nice one.

That puts:

using(new UnitOfWork()) {} back on the table - instead of Within.UnitOfWork(anon delegate etc).

Alex Henderson
06/20/2007 12:56 AM by
Alex Henderson

That's some sweet stuff :) how come I haven't heard of it until now!

Derik Whittaker
06/20/2007 01:08 AM by
Derik Whittaker

Ok,

What am i missing? In what scenario is this needed?

Jeff Brown
06/20/2007 01:10 AM by
Jeff Brown

Oh neat! Mind that I think it can be fooled... What happens if your using statement appears within an exception handler? Will you be able to tell whether the exception occurred within or outside the exception handler?

Jeff Brown
06/20/2007 01:11 AM by
Jeff Brown

Doh! Meant within the using statement.

Ayende Rahien
06/20/2007 04:30 AM by
Ayende Rahien

@Derik,

It enable this code:

using(new Transaction())

{

/// blah

}

Where the transaction can decide if it wants to commit or rollback based on the exception status.

Jeff Perrin
06/20/2007 04:54 AM by
Jeff Perrin

Derik,

Previously to do a "using" with a transaction you had to do something like this:

using(Transaction t = new Transaction()){

//important stuff

t.VoteToCommit();

}

Using this technique it looks like you don't need the VoteToCommit() call, which is good because I always forgot to add that, and then spent hours debugging why my tables weren't being updated.

Gokhan
06/20/2007 07:20 AM by
Gokhan

Then why don't you write the code in the post using the use case, in the first place?

There's so much going on in comments on this site, all the time, and you write 187 posts a day, average. When wife and kid start to complain, I'll give them your e-mail :)

Jeff Brown
06/20/2007 09:21 AM by
Jeff Brown

@Jeff Perrin,

Yep, I've forgotten Transaction commits too. Wouldn't it be nice to have Smalltalk blocks in C# instead of these clunky control structures? Anonymous delegates and even lambdas don't quite cut it. Oh well.

Still, the exception detecting using trick will be very useful indeed... My only concern is that its behavior may surprising to the unwary.

Jeff Brown
06/20/2007 09:58 AM by
Jeff Brown

One last thought...

Is this hack portable? What will Mono do? I assume it doesn't necessarily implement its exception handling using Win32 SEH. Therefore I wouldn't be too surprised if the Marshal.GetExceptionCode() method were unimplemented or not meaningful...

Stefan Wenig
06/20/2007 10:36 AM by
Stefan Wenig

ayende, this is what MSDN lib says:

GetExceptionCode is exposed for complier support of structured exception handling (SEH) only. If called before an exception is thrown, this method returns 0xCCCCCCCC.

Ok, so firstly, == 0 seems to be wrong. Then, which exception? The last exception on this thread? The innermost exception in the current scope? The exception in the innermost scope? You'd have to make and test a whole lot of assumptions here.

What about portability? Mono?

I have to say I'd rather write VoteToCommit() than build on such weak assumptions...

Bill Pierce
06/20/2007 12:38 PM by
Bill Pierce

I'm a bit slow to catch on as well. Is the intention of ExceptionDectector to allow you to dispose differently depending on wether or not an exception was thrown? I would need a use case to more fully understand this feature.

@inoodle:

I would contend that Within.UnitOfWork is a much clearer syntax, even with delegate { } tacked on the end.

@Jeff Perrin:

What if you do not want to implicitly commit the transaction if no exception is thrown? The current functionality of SqlTransaction is to implicitly Rollback if no Commit was issued. I would prefer more descriptive syntaxt like using(Transaction trans = new Transaction(CommitStyle.Implicit)) { }

What is my feeble brain missing?

Ayende Rahien
06/20/2007 02:17 PM by
Ayende Rahien

You aren't missing much. That is the intent of the code.

The ideas behind With.Transaction, etc are to handle the case of thrown exception, so having a good way to handle that is very nice.

John Rusk
06/20/2007 07:12 PM by
John Rusk

I wonder why Microsoft did not use this for System.Transactions? Do they know something we don't? Did they simply not think of it? Or, did they discard it because they felt that a more explicit syntax was easier for us to understand?

Ayende Rahien
06/20/2007 07:26 PM by
Ayende Rahien

I would go with the more explicit syntax reason

Bevan Arps
06/20/2007 08:36 PM by
Bevan Arps

Provided this is reliable (something I'm not yet convinced of), this is going to simplify a whole bunch of things in my codebase.

Interestingly, you could do this in a supported way in Delphi, and I was talking to a friend recently to see if it was possible in .NET.

I wonder, does referencing Marshal introduce any odd dependencies that wouldn't normally be present? If so, this could explain why MS didn't use it themselves in System.Transactions.

inoodle
06/25/2007 11:32 AM by
inoodle

Ah, just spotted a potential issue for me at least...

Marshal.GetExceptionCode() won't work under asp.net medium trust.

Back to delegates :)

Comments have been closed on this topic.