Reputation:
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
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
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
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