user7450900
user7450900

Reputation:

Is it possible to extend the 'using' block in C#?

Is there a way to extend the using block in C# in such a way that takes a delegate as a second parameter alongside an IDisposable object and executes every time when an exception is thrown inside that using block?

Imagine we have a delegate, something like this:

public delegate void ExceptionHandler(Exception ex);

And suppose I have a method that matches that delegate, something like this:

public void Log(Exception ex)
{
  // Some logging stuff goes here
}

And I want to accomplish something like this:

using(SqlConnection connection = new SqlConnection(""), Log)
{

}

Is there a way to extend C# in such a way?

Upvotes: 17

Views: 2863

Answers (3)

Sefe
Sefe

Reputation: 14007

A using block is a shorthand for a try finally block with a call to Dispose in the finally. It can not be extended to be something more than that. What you want is the functionality of a try catch finally, so why not use exactly that:

SqlConnection connection = new SqlConnection("");
try {

}
catch (Exception exc) {
    Log(exc);
}
finally {
    connection.Dispose();
}

This comes with all the advantages of a try catch finally, for example catching multiple exception types and C# 6.0 exception filters. Consider this:

SqlConnection connection = new SqlConnection("");
try {

}
catch (SqlException exc) when (exc.Number > 0) {
    //Handle SQL error
}
catch (Exception exc) {
    Log(exc);
}
finally {
    connection.Dispose();
}

If you want to reuse standardized try catch finally blocks, you can use delegates:

static class ErrorHandler {
    public static ExecuteWithErrorHandling<T>(Func<T> createObject,
        Action<Exception> exceptionHandler, Action<T> operation) where T : IDisposable {

        T disposable = createObject();
        try {
            operation(disposable);
        }
        catch (Exception exc) {
            exceptionHandler(exc);
        }
        finally {
            disposable.Dispose();
        }
    }
}

Which you can call like:

ErrorHandler.ExecuteWithErrorHandling(() => new SqlConnection(""), Log, connection => {
    //Use connection here
});

Upvotes: 34

Zohar Peled
Zohar Peled

Reputation: 82484

You can't extend the using statement but you can wrap it in a method:

void DoStuff(Action action, ExceptionHandler log)
{
    using(var connction = new SqlConnection(""))
    {
        try
        {
            action();
        }
        catch(Exception e)
        {
            log(e)
        }
    }
}

Upvotes: 13

Jon Hanna
Jon Hanna

Reputation: 113292

Just step back a bit with the syntactic sugar.

Since:

using(var obj = factory_or_constructor())
{
  // Do Stuff
}

is shorthand for the common pattern

obj = factory_or_constructor();
try
{
  // Do Stuff
}
finally
{
  ((IDisposable)obj)?.Dispose();
}

Then you could just change it to:

try
{
  // Do Stuff
}
catch(Exception ex)
{
  Log(ex);
  throw;
}
finally
{
  ((IDisposable)obj)?.Dispose();
}

But then it doesn't really offer much more over the simpler and clearer.

using(var obj = factory_or_constructor())
{
  try
  {
    // Do Stuff
  }
  catch(Exception ex)
  {
    Log(ex);
    throw;
  }
}

It's not really "extending using" but then if the point of using is to have a succinct syntax for a common pattern, it isn't as useful for it to have a succinct syntax for a rare pattern.

Upvotes: 4

Related Questions