This project is read-only.

Add exception capture to the using statement

Topics: C# Language Design
May 4, 2014 at 2:38 PM
At the moment the using statement is often abused to perform some work when exiting a block. Like this:
using (new TraceScope("SomeMessage") {
 DoSomething();
}
This would result in two messages:
Enter, SomeMessage, 10:00AM
Exit, SomeMessage, 10:01AM
We can capture both enter an exit. Therefore also the duration. Using thread-local state we can also capture a hierarchy of TraceScopes. (To be clear, logging is just an example here).

One thing that we can't capture is an exception! Our log can never log the exception. We'd need something like this:
using (var s = new TraceScope("SomeMessage") {
 try {
  DoSomething();
 }
 catch (Exception ex) { s.SetException(ex); throw; }
}
That is quite convoluted. I propose that the using block be extended to support not just IDisposable but also a new interface that can receive an eventual exception:
interface IBlock<TException> where TException : Exception {
 void OnExit(TException ex = null);
}
This way, we can add compensation logic or logging work easily with the using statement.

OnExit could even return a value indicating what to do: rethrow, or swallow, or throw something else. We could use this to add complex error handling logic:
using(new SuppressSqlTimeoutExceptions()) {
}
Maybe the OnExit method could even signal that it wants the block to be reexecuted:
using(var retry = new RetryOnError(maxCount: 3)) {
 Console.WriteLine("This is try number " + (retry.RetryCount + 1));
 if (retry.RetryCount == 0)
  throw new SqlException();
}
RetryOnError would be a class that signals to reexecute if an exception (or even a certain exception) occurred. This would be quite generally usable.

In fact we could create quite general control flow structures from this.

We can already do that with passing a lambda to a helper function but that never seems to integrate so well in more complex methods. It also looks quite convoluted in case of longer multi-statement lambdas. And they tend to nest off the page.
May 4, 2014 at 5:10 PM
IMHO, what's more important is not so much being able to "catch" an exception (such that it doesn't propagate up the call stack) but rather letting code know (including in the Dispose) know what exception has happened. My suggestion would be
interface IDisposableExOnly  { void Dispose(Exception pendingException); }
interface IDisposableEx : IDisposable, IDisposableEx {}
with the semantics that the argument to a using block may implement IDisposableExOnly and, if it does, the pendingException parameter would receive null if the block completed successfully, or the pending exception if it was exit via exception.

A class which implements IDisposableEx would be valid in using blocks for older and newer C# compilers; those implementing IDisposableExOnly (but not IDisposable) would only be allowed in newer compilers (for cases where the resource couldn't achieve proper semantics without knowing why the surrounding block was exit).

Being able to have a using block include other clauses would be nice, but would not offer the semantic possibilities of having cleanup code know about pending faults.
May 5, 2014 at 1:28 PM
While I totally agree that using statement could be enhanced, I strongly disagree the new interface should have IDisposable in its name. Concept of disposing resources does not require any additions. Scoped operations do. It's unfortunate that scoped operations are currently implemented by abusing IDisposable. For more detailed discussion please go to Pattern based using
May 6, 2014 at 2:08 PM
IMO, you should use tools like postsharp instead.
May 6, 2014 at 2:59 PM
Przemyslaw wrote:
While I totally agree that using statement could be enhanced, I strongly disagree the new interface should have IDisposable in its name. Concept of disposing resources does not require any additions. Scoped operations do. It's unfortunate that scoped operations are currently implemented by abusing IDisposable. For more detailed discussion please go to Pattern based using
Looking briefly at that other thread, I don't see why Close is better than Dispose. The term Dispose is perhaps slightly misleading; I would describe the proper function in English as "Be informed that services are no longer required, and you may be subject to abandonment without further notice; use that information as you see fit." To justify the term Dispose, I could word that as "Put your affairs in order, such that you may be safely abandoned and consequently annihilated. Your services are no longer required." Although Unemploy might be a better verb, I think Dispose is pretty well established as meaning that.

Further, there are many situations where Dispose even in its traditional sense should (but presently cannot) behave differently depending upon whether an exception is pending. For example, if proper usage of a database connection requires that every transaction which is begun must be either committed or expressly rolled back, then closing the connection without having done a commit or rollback first should trigger an exception unless the transaction was abandoned because of an exception. If a transaction was abandoned because of an exception, having its Dispose method destroy all record of what the earlier exception had been would be obnoxious. Would you disagree that closing a connection in a fashion inconsistent with its required usage should trigger an exception if no other exceptions are pending? Do you know any clean way to implement that without letting the connection know at the time of its dismissal what exception if any is pending?
May 7, 2014 at 10:56 AM
English is not my first language, so I am not able to talk about semantic meaning of English names. I will not fight for "Close". I just want other method than "Dispose". Dispose always mean to me "Free all your resources in a humble way and prepare to be garbage collected". The reasons I want other method is to separate resource cleaning from scoped operations. Both use cases differ - e.g. disposing resources should not throw, while scoped operation is free to throw if necessary. Disposed object is probably going to be abandoned, while scoped operation can be freely reused as many times as needed, etc.

IDisposable docs refer only to concept of disposing resources. When people teach about this interface, they very often limit it to the concept of disposing resources. All this makes implementing IDisposable to get semantics of scoped operation a hack. It can trick casual programmer. He can have good intentions to "fix" the perfectly good code so that it fits everything he learned about IDisposable. As a result, subtle bugs cripple in.

Regarding the exceptions handling. While I am not big fan of throwing from Dispose(), this is only a guideline, not a strict rule. As I understand, connection is already aware of existing transactions, so that it can check if there are any pending. Can't you just read the "abandoned from exception" state from transaction object and act accordingly?
May 7, 2014 at 6:32 PM
Przemyslaw wrote:
English is not my first language, so I am not able to talk about semantic meaning of English names. I will not fight for "Close". I just want other method than "Dispose". Dispose always mean to me "Free all your resources in a humble way and prepare to be garbage collected". The reasons I want other method is to separate resource cleaning from scoped operations. Both use cases differ - e.g. disposing resources should not throw, while scoped operation is free to throw if necessary. Disposed object is probably going to be abandoned, while scoped operation can be freely reused as many times as needed, etc.
The rule against throwing from Dispose stems from the fact that if Dispose is called while unwinding the stack from an exception, having Dispose throw an exception would cause the earlier exception to be lost. Eliminating that problem would eliminate the need for the rule. Instead, the rule would be "throw an exception if the cleanup was unable to leave things in the state the caller expects". In many situations, such as with buffered files, a call to Dispose may need to physically write data which the caller is expecting to be on the disk. Dispose should not allow control to return normally to the caller unless data actually gets written. If the Dispose occurs while code is unwinding from another exception, the caller really should be informed of both problems.

Neither resource-bearing nor scope-delineating objects are generally reused after disposal. Even if a scope-controlling object can be reused, the normal pattern is to ask that object for a lightweight scope-wrapping object at the start of each using block, and then dispose and abandon the wrapper object at the end. The unfortunate way C# treats structures in using makes the creation and abandonment of scope blocks slightly more expensive than it should be, but one-time-use wrappers are still the preferred idiom.
IDisposable docs refer only to concept of disposing resources. When people teach about this interface, they very often limit it to the concept of disposing resources. All this makes implementing IDisposable to get semantics of scoped operation a hack. It can trick casual programmer. He can have good intentions to "fix" the perfectly good code so that it fits everything he learned about IDisposable. As a result, subtle bugs cripple in.
Where have you see "resources" defined? I would say that an object George acquires a resource if it asks something somewhere to put itself into a state which is beneficial to George but may be detrimental to other entities. For example, a File object may ask the file manager to associate a handle with a file and disallow access to that file by anyone who doesn't have that handle. Most kinds of scope-guard objects ask the controller of a scope to grant permission to do something; disposing the received token says the permission is no longer required.

Even if the guard is lazy and unconditionally tells everyone they have permission, so the act of requesting permission and being told to proceed wouldn't prevent anyone else from doing likewise, the permission may still be viewed as an "untracked" resource. At any given time, only one execution context at a time can be in a position to modify the object without causing corruption. The fact that a guard may tell a thread that it has permission to modify an object while another thread is doing so doesn't mean the latter thread can safely modify the object without corruption. It merely makes it harder for the second thread to avoid erroneously modifying the object in such as way as to corrupt it.
Regarding the exceptions handling. While I am not big fan of throwing from Dispose(), this is only a guideline, not a strict rule. As I understand, connection is already aware of existing transactions, so that it can check if there are any pending. Can't you just read the "abandoned from exception" state from transaction object and act accordingly?
Presently, the framework has no way of knowing whether a transaction object was abandoned because of an exception, or because the programmer forgot to commit. Normal design is to silently assume that any abandoned transaction should be rolled back, but when there's no exception pending the rollback really shouldn't be silent.