Using Blocks That Don't Execute Dispose() After Exceptions

Topics: C# Language Design
Jul 16, 2014 at 9:39 PM
This is totally syntactic sugar, but it would be nice in a lot of cases, particularly ones where database access is involved, to have some sort of using block that doesn't execute when an exception is thrown. Let's call it, for the sake of argument, softusing. That's not a great name, but it'll help me explain.

I do a fair bit of coding against third party files, particularly containing county records, that get updated on an unknowable schedule and take a long while to read through. To avoid reading unchanged data over and over, I have two functions: one checks the database to see whether the given file modified date is after the last time I read the file, and the other sets that access date in the database.

A few times now, while writing out this pattern, I've realized "wait! I can do this with a using block!' I could return an IDisposable with a bool for ShouldRead, then I can throw that in a using and be sure that I don't forget to update the database when I'm done.
using (var fileShouldRead = method(fileName, fileReadTime))
{
    if(fileShouldRead.ShouldRead)
    {
        // Do stuff
    }
}
However, as it occurs to me every single time I think of this, I can't do that because it'll call that IDisposable.Dispose() if I hit an exception, which is really the biggest reason I can't update the database at the start.

I know I'd be somewhat cheating the idea of IDisposable, since I'm not actually releasing any resources, but it's a perfect pattern with that exception.

Thus, it would be fantastic to have something that works for cases like this, where Dispose is called only when the softusing block is executed in full. In synonymous code, it would be the difference of:
IDisposable disp = ...;

try
{
    // do whatever is inside the using block

    // this is where my new form of using block would dispose of disp
}
finally
{
    // this is where the current using block would dispose of disp
}
That way, if something went awry, I wouldn't count myself as having read the file in full. I would know to try again.

The other time that jumps to mind where this could be useful is when using SqlTransactions. You could spin one up and call some method (probably through a wrapper class) called BeginTransaction that returns an IDisposable, then put that in a softusing block, and when Dispose was called, you'd know to commit the transaction. You'd know you hadn't exited out via an exception of any kind.

Again, this isn't really a language feature that we can't already use--the way I've written these things works, where you simply call two methods, but it would be nice to have some way to be more sure that I always remember to do that.
Jul 16, 2014 at 10:52 PM
Edited Jul 16, 2014 at 10:54 PM
I'm sure I don't understand something about your suggestion, can you tell me how this simple static method would work different than your new using?
public static void MaJaUsing<T>(T disposable, Action<T> action) where T : IDisposable {
    action(disposable);
    disposable.Dispose();
}
MaJaUsing(method(fileName, fileReadTime), fileShouldRead => {
    if(fileShouldRead.ShouldRead) {
        // Do stuff
    }
});
Jul 17, 2014 at 1:23 AM
sruppr wrote:
...can you tell me how this simple static method would work different than your new using?
That would work, but as I said I'm looking mostly for syntactic sugar here. At least in my code, there are quite a few places and scenarios where a pattern like I'm talking about makes sense, so it would be nice to have it a bit more innately supported. Calling your,
var fileShouldExecute= FileReadDates.GetShouldExecute(filename, lastModified);
MaJaUsing(fileShouldExecute, value =>
                                            {
                                                if(value.ShouldExecute)
                                                {
                                                    // Read the file
                                                {
                                            });
Just seems a lot messier than something that would let me do
softusing (var fileShouldExecute = FileReadDates.GetShouldExecute(filename, lastModified))
{
    if(fileShouldExecute.ShouldExecute))
    {
        // Read the file
    }
}
Again, I really can't stress enough that this is possible through language features that exist, but in fairness to that argument, so is the using block as it stands now:
public static void RegularUsing<T>(T disposable, Action<T> action) where T : IDisposable {
    try {
        action(disposable);
    }
    finally {
        disposable.Dispose();
    }
}
Correct me if I'm wrong--and I might be, it's been a long day--but that would perform the same operation as what a good ol' fashioned using block does now. I'd still like to see something that made my case easier.

But maybe I'm alone in using this pattern, with transactional tests like this. I might be. In that event, it's not a useful suggestion. I could accept that as well.
Jul 17, 2014 at 4:24 AM
What I'd like to see would be a means via which a using-associated interface would be informed whether the block exited normally or because of an exception. Transactions shouldn't be abandoned in case of exception, but may benefit from knowing whether they were not committed because code "forgot" to do a commit (an exception probably should be thrown in that case) or because an exception occurred (in which case an exception should definitely not get thrown).
Jul 17, 2014 at 8:57 PM
That's true, that would be very useful. Something like this, but with better names.
interface ExtendedIDisposable
{
    void SuccessfulDispose();
    void ExceptionDispose(Exception ex);
}
Then the using block could work with either, and call the appropriate method for the exit circumstances if the passed argument implemented that interface.