是否可以在 C# 中扩展 'using' 块?

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

有没有一种方法可以扩展 C# 中的 using 块,将委托作为第二个参数与 IDisposable 对象一起使用,并在每次内部抛出异常时执行 using块?

假设我们有一个代表,像这样:

public delegate void ExceptionHandler(Exception ex);

假设我有一个匹配该委托的方法,如下所示:

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

我想完成这样的事情:

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

}

有没有办法以这种方式扩展 C#?

一个using块是shorthand一个try finally块调用finally中的Dispose。它不能扩展到不止于此。你想要的是 try catch finally 的功能,所以为什么不完全使用它:

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

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

它具有 try catch finally 的所有优点,例如捕获多种异常类型和 C# 6.0 异常过滤器。考虑一下:

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

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

如果你想重用标准化的 try catch finally 块,你可以使用委托:

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();
        }
    }
}

你可以这样称呼:

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

您不能扩展 using 语句,但可以将其包装在一个方法中:

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

稍微退后一步,使用语法糖。

开始于:

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

是 shorthand 的常见模式

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

那么你可以把它改成:

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

但它并没有真正提供比更简单和更清晰的更多内容。

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

这不是真的 "extending using" 但是如果 using 的目的是为通用模式提供简洁的语法,那么为稀有图案。