Is it possible to tell the difference between the normal exit of a using
block and an exit that is due to an exception being thrown? Using try/catch/finally I can write:
Foo f = new Foo();
try
{
f.stuff();
}
catch()
{
f.ThereWasAnError();
}
finally
{
f.Dispose();
}
but it would be nice if the users of my class didn't need to care about this, and could write
using (var f = new Foo()) { ... }
I can't see a way to t开发者_开发问答ell in the Dispose method why I'm being called. Is it possible?
It would be helpful if you'd explain what you're trying to do. I suspect you are trying to do something like this:
using(t = new Transaction())
{
DoStuff(t);
}
where the implementation of Transaction.Dispose() you want to write looks like this:
void Dispose()
{
if (I'm being disposed because of an exception)
Roll back the transaction
else
commit the transaction
}
That's an abuse of the disposable pattern. The purpose of the disposable pattern is to politely release unmanaged resources as soon as possible regardless of why the object is being disposed. It's not to implement transaction semantics. If you want to have transaction semantics then you're going to have to write the code yourself; that pattern isn't built into the C# language. The way to write that is:
Transaction t = new Transaction();
try
{
DoStuff(t);
}
catch
{
t.Rollback();
throw;
}
t.Commit();
(Keeping in mind of course that there could be a thread-abort exception thrown before the commit but after the DoStuff; if that bothers you, then you're going to need to write constrained execution region code.)
If you want to make it easier on your users then encapsulate that in a higher-order helper method:
static void WithTransaction(Action<Transaction> action)
{
Transaction t = new Transaction();
try
{
action(t);
}
catch
{
t.Rollback();
throw;
}
t.Commit();
}
....
WithTransaction(t=>DoStuff(t));
If Dispose()
has to know if there was an error or it has to be called only if everything went fine, then it is a bad Dispose
implementation.
Dispose
is not about error handling, it is about ensuring that in any condition your unmanaged resource gets released.
You would do your error handling as usual but put using
block inside the try
block:
try
{
using(...)
{
...
}
}
catch(Exception e)
{
...
}
Is it possible to tell the difference between the normal exit of a using() block and [...] an exception being thrown?
That's not the purpose of a using block, so: No.
catch()
{
f.ThereWasAnError();
}
If you really want some form of error handling inside your objects, than built it in. It shouldn't need outside help. That would probably require a try/catch in every public method, but that;s the price.
But I would be very suspicious of such a design.
I do not recommend the following. As Eric Lippert writes, this is an abuse of the disposable-pattern.
That being said, the following should work:
public void Dispose(){
if(Marshal.GetExceptionCode()==0){
// disposing normally
Commit();
}else{
// disposing because of exception.
Rollback();
}
}
Not that I know of, but on first glance, I would skip the catch block and let the error bubble up. YMMV.
There isn't any way to have CLR tell your Dispose
method that an exception is about to be thrown.
But the strange thing is that you want to "notify" your own Foo
class about an exception which occurred in its own Stuff
method. Doing that is very unusual; have you ever seen a class which you need to notify that it just threw an exception?
You should use the IDisposable
pattern to do some unmanaged cleanup after your class is not needed anymore. From your question, I suspect that your class performs lots of actual functionality inside the Dispose
method.
use try{}catch{} inside using()block.
Well. If, when you are disposing, you only care whether an exception was thrown by the instance being disposed, you could cache the exception:
class Foo
{
Exception _thrownException;
void Stuff()
{
try
{
//...
}
catch (Exception e)
{
_thrownException = e;
throw;
}
//... I suppose you get the idea from here.
However, this would not let you know if something else in the using block threw the exception, for example:
using (var f = new Foo())
{
int i = f.IntegerMethod() / 0;
...
}
I have done
using(Foo f = new Foo())
{
f.stuff();
f.IsOk();
}
Then the dispose method knows if it was called due to an exception or just hidding the end of the "try" or "using" block.
Sometimes "IsOk()" is called "Commit()"...
精彩评论