什么是安全返回构建的IDisposables的最佳方法?

Eam*_*nne 8 c# idisposable

编辑:下面显示了两个选项.

如果您只是使用 IDisposable提供的功能,那么恰当命名的using子句可以正常工作.如果你在一个对象中包装一个IDisposable对象,那么包含对象本身就需要IDisposable,你需要实现适当的模式(密封IDisposable类,或者更混乱,但标准virtual模式).

但有时辅助工厂方法有利于清洁.如果你IDisposable在构造之后直接返回,你没问题,但是如果你先构造它然后修改它或者执行在返回之前可以抛出异常的代码,你需要安全地调用.Dispose()- 但是只有在出现错误时才需要.

例如,不安全的代码可能看起来像这样......

DbCommand CreateCommandUnsafely(string commandText)
{
    var newCommand = connection.CreateCommand();
    newCommand.CommandText = commandText;  //what if this throws?
    return newCommand;
}    
Run Code Online (Sandbox Code Playgroud)

解决方案遵循以

DbCommand CreateCommandSafelyA(string commandText)
{
    DbCommand newCommand = null;
    bool success = false;
    try    {
        newCommand = connection.CreateCommand();
        newCommand.CommandText = commandText; //if this throws...
        success=true;
        return newCommand;
    } finally{
        if (!success && newCommand != null )
            newCommand.Dispose(); //...we'll clean up here.
    }
}


DbCommand CreateCommandSafelyB(string commandText)
{
    DbCommand newCommand = null;
    try    {
        newCommand = connection.CreateCommand();
        newCommand.CommandText = commandText; //if this throws...
        return newCommand;
    } catch {
        if (newCommand != null)
            newCommand.Dispose(); //...we'll clean up here.
        throw;
    }
}
Run Code Online (Sandbox Code Playgroud)

安全变体A只是一行,但似乎是惯用的方法.似乎没有任何真正简洁的解决方案,尽管下面的一些海报提供了一些使用lambda的选项来提取封装这个逻辑.

使用上述任何安全方法的代码膨胀仍然存在,并且使用最初看起来像的代码尤其令人恼火......

return new MyDisposableThing {
    OptionA = "X",
    OptionB = B.Blabla,
    Values = src.Values.Where(priority => priority > 1.0),
};
Run Code Online (Sandbox Code Playgroud)

安全编写的上述代码相当长,可读性较差,因为您无法再安全地使用缩短的setter语法.

Mat*_*ias 7

不 - 我认为没有更好的方法.

但是,您可以编写一个帮助程序类:

public static class DisposeHelper
{
  public static TDisposable DisposeOnError<TDisposable>(TDisposable dispoable, Action<TDisposable> action)
     where TDisposable : IDisposable
  {
    try
    {
       action(dispoable);
    }
    catch(Exception)
    {
       disposable.Dispose();
       throw;
    }

    return disposable;
  }
}
Run Code Online (Sandbox Code Playgroud)

所以你可以写:

return DisposeHelper.DisposeOnError(connection.CreateCommand(), cmd => cmd.CommandText = commandText);
Run Code Online (Sandbox Code Playgroud)

但是,我不确定这是否真的是一种更好的方法.


Jef*_*dge 4

我相信这是标准模式:

DbCommand CreateCommand(string commandText)
{
    DbCommand newCommand = null;
    bool success = false;
    try
    {
        newCommand = connection.CreateCommand();
        newCommand.CommandText = commandText;
        success = true;
        return newCommand;
    }
    finally
    {
        if (!success & newCommand != null)
            newCommand.Dispose();
     }
}
Run Code Online (Sandbox Code Playgroud)

它不会捕获并重新抛出错误。