返回 IDisposable 对象并且仅在异常时释放的最佳方法?



我一直在寻找答案,但我无法真正找到我正在实现的确切情况。我想在方法中返回 IDisposable 对象,而不在同一方法中释放/关闭。但是我想仅在引发异常时才释放对象。 在这种情况下,最好的处理方法是什么?以下是我目前处理此案的方式。

public DisposableObject GetObject()
{
DisposableObject object = null;
try
{
object = new DisposableObject();
object.Open();
}
catch
{
if (object != null)
{
object.Close();
}
throw;
}
return object;
}

从上述方法创建的对象将被其他类似方法使用和处置。

public void OtherMethod()
{
using(var object2 = GetObject())
{
DoSomethingWithObject2(object2);
}
}

在这种情况下还有其他选择吗?

就像莫顿说的,问题在于在工厂方法中的DisposableObject实例上调用Open,但我不同意腐蚀性解决方案。

真正的问题是Open是一个泄漏的抽象。为什么要实例化无效实例?当我想要一个文件流时,我调用File.Open(...),我不实例化一个新的Stream并调用OpenFile(...)。这将迫使我知道如何打开文件,并完全破坏工厂方法的目的(是的,File.OpenStream实例的工厂方法(。

因此,当我读取文件时,我是这样读取的:

using (var stream = File.Open("myFile.text", FileMode.Open))
using (var reader = new StreamReader(stream))
{
var content = reader.ReadToEnd();
Console.WriteLine(content);
}

如果文件无法正确打开,我不必担心如何关闭文件。事实上,如果Open调用抛出异常,using语句甚至不能在stream上调用Dispose(),因为尚未分配流,执行流甚至尚未进入using语句的块范围。

public class Program
{
public void Main()
{
try
{
using (var obj = MyObj.Create())
{
// everything is already in a valid state.
var message = obj.ReadMessage();
Console.WriteLine(message);
}
}
catch (Exception ex)
{
// If the factory succeeded, the using statement did the cleanup.
// if the factory failed, the factory took care of the cleanup.
Console.WriteLine(ex.Message);
}
}
}

public class MyObj : IDisposable
{
public static MyObj Create()
{
Stream stream = null;
StreamReader reader = null;
try
{
stream = File.Open("myFile.txt", FileMode.Create);
reader = new StreamReader(stream);
return new MyObj(reader);
}
catch
{
reader?.Dispose();
stream?.Dispose();
throw;
}
}
private readonly StreamReader _reader;
private MyObj(StreamReader reader)
{
_reader = reader;
}
public string ReadMessage()
{
return "the message: " + _reader.ReadToEnd();
}
public void Dispose()
{
_reader?.Dispose();
}
}

我想我宁愿把打开和关闭连接的所有责任推给这个对象的用户而不是工厂。

您展示的当前方法,如果不创建实际连接,就无法创建实例,例如测试。此外,该方法是否不透明,因为另一个人不会假设要打开的连接。

我发布的方法使代码的用户能够更轻松地处理打开DisposableObject时可能发生的错误。

public DisposableObject GetObject() {
return new DisposableObject();
}
public void OtherMethod() {
using (DisposableObject o = GetObject()) {
try {
o.Open();
} catch (Exception ex) {
// Log(ex);
} finally {
// If not called within the dispose function.
o.Close();
}
}
}

不过,如果你想处理开口,你的方法没问题。但是,我会更改一些小细节:

public DisposableObject GetObject() {
var o = new DisposableObject();
try {                
o.Open();
return o;
} catch (Exception ex) {
o.Dispose(); // o.Close is called within.
throw ex;
} 
}

最新更新