1

I'm trying to refresh my memory on C# and using pattern. Is there a possibility to have some control flow be executed automatically on exception?

For instance:

class Test : IDisposable
{
     public void Dispose()
     {
         Console.WriteLine("ok");
     }

     public void XX()
     {
         Console.WriteLine("KO");
     }
}

using (new Test()) 
{
}
// prints "ok"


using (new Test())
{
    throw new Exception();
}
// this would print "KO"

Is there a way to achieve this effect in C# ? Example of use case would be : a database system where I want to commit result on correct execution, but rollback on exception

For example, currently to handle transaction commits/rollback, it's possible to do this :

using(var tran = conn.BeginTransaction()) 
{
    try 
    {
        // DO WORK
        tran.Commit();
    } catch {
        tran.Rollback();
        throw;
    }
}

But this means that clients of "tran" object have to correctly write the try/catch code. I'm looking for a way to provide the same functionality out of the box, so that users just have to write the "DO WORK" code (and the "using") and not have to write the "using" and the try/catch code

15
  • Do you understand why your current code would print "ok" twice? Commented Jul 26, 2022 at 20:02
  • @gunr2171 yes I understand why. What I'm trying to find is if there is an elegant way to do the behavior I wrote without requiring boilerplate Commented Jul 26, 2022 at 20:08
  • Dispose runs when you leave the scope. Doesn't matter how that happens. Is there something more advanced that a try/catch can't solve? Commented Jul 26, 2022 at 20:11
  • @gunr2171 the "pattern" I'm looking for would be that the Test class handles the try/catch logic, and not the user of the Test class Commented Jul 26, 2022 at 20:12
  • @lezebulon Why can't you create one method that accepts an Action delegate and wrap that inside the try/catch you have in your example? The user would then provide the DoWork via Action delegate and you would handle the try/catch for them. Commented Jul 26, 2022 at 20:22

1 Answer 1

1

What you want isn't 100% achievable. IDisposable is not designed to handle exceptions. It's designed to free up a resource when that resource goes out of scope.

SqlTransaction does something similar. You create a using scoped variable, and if you don't explicitly commit the transaction, it will rollback the transaction.

using (var transaction = connection.BeginTransaction())
{
   // ...
   transaction.Commit();
}

You could emulate something like this, and require the user to say if the body of the using block was successful or not.

public class Test : IDisposable
{
    private bool Successful { get; private set; } = false;

    public void Dispose()
    {
        if (Successful)
            // ...
        else
            // ...
    }

    public void Success() => Successful = true;
}
using (var test = new Test())
{
    // ...
    test.Success();
}

Just note, you cannot get the exception information from Dispose. You also can't be sure if an exception was thrown or the user forgot to call .Success() when Successful is false.


Instead, you really need to do error handling inside the using scope.

Sign up to request clarification or add additional context in comments.

1 Comment

Ok I see Indeed your alternate version is something I had in mind, but in any case it doesnt change much whether the user has to call either Success() or Commit() at the end

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.