在实现IDisposable的类上正确使用Dispose方法

时间:2011-03-29 01:00:15

标签: c# dispose idisposable

我今天正在处理一些使用System.Net.Mail.MailMessage类的代码,如此

public MailMessage CreateMessage(string fromAddress, string recipient)
{
    MailMessage message = new MailMessage(fromAddress, recipient);
    message.Subject = subject;
    message.Body = body;
    return message;
}

忽略了这种方法的微不足道的性质,我得到了编译器警告说

  

对象'消息'未随附   所有异常路径。呼叫   System.IDisposable.Dispose on object   所有引用之前的'message'   超出范围。

这很有意思,因为编译器警告消息在超出范围之前不会被处理掉,但我会假设返回对它的引用意味着虽然消息变量超出范围,但仍然是对底层对象的引用,在这种情况下,我非常怀疑我会想要处理它。

这让我有点困惑,因为警告信息的含义是你不应该返回一次性物品。这真的是这种情况还是只是编译器警告的情况变得疯狂了?

2 个答案:

答案 0 :(得分:7)

此警告的含义是,如果方法抛出(例如在Subject setter中),您可能会留下一个没有任何引用的无关MailMessage

你应该通过以下方式防止这种情况发生:

public MailMessage CreateMessage(string fromAddress, string recipient)
{
    MailMessage message = new MailMessage(fromAddress, recipient);
    try {
        message.Subject = subject;
        message.Body = body;
        return message;
    }
    catch {
        if (message != null) {
            message.Dispose();
        }
        throw;
    }
}

编译器没有任何反对返回IDisposable实例的内容:)

答案 1 :(得分:1)

我见过的一种有时可能有用的模式是创建一个名为DisposeWrapper< T>的对象。它包含构造函数中提供的类型为T的IDisposable对象,它支持两种方法:Keep和Dispose。在不调用Keep的情况下调用Dispose将在包装的IDisposable上调用Dispose;调用Keep将阻止Dispose击中IDisposable。

为方便起见,使用具有DisposeWrapper< T>的通用工厂方法的静态非泛型DisposeWrapper类可能很方便。允许编译器使用方法类型推断来推断包装类的类型。

因此可以做一些像[我习惯于使用vb语法而不是c#的话,所以如果这不是正确的话,请道歉):

{
  using(var wrapper = DisposeWrapper.Create(new SomeDisposableThing))
  {
    ... do some stuff with wrapper.Value;
    return wrapper.Keep();
  }
}

如果代码到达“return wrapper.Keep();”,那么将返回wrapper.Value而不会丢弃。如果代码在不调用wrapper.Keep的情况下退出Using语句,则将处理wrapper.Value。

当编译器和工具坚持认为必须捕获代码可以在不清理IDisposable的情况下逃脱的所有可能方式时(尤其是在C#中无法安全地使用具有IDisposable字段的字段初始化程序时)时,有时会很烦人但我认为包装IDisposable应该是让编译器开心。

另一种模式是拥有一个包含IDisposable列表的包装器,并使用通用方法在创建IDisposable时“注册”它们(该方法可以将新的IDisposable作为其适当的类型返回)。这可以是一个在构造函数中使用的好模式;通过一些工作,它也可以用于vb.net中的字段初始化器。