【问题标题】:Correct usage of Dispose method on class that implements IDisposable在实现 IDisposable 的类上正确使用 Dispose 方法
【发布时间】:2011-03-29 01:00:14
【问题描述】:

我今天正在处理一些使用 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 对象 'message' 在所有对它的引用之前 超出范围。

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

这让我有点困惑,因为警告消息的含义是您不应该退回一次性物品。真的是这样,还是只是编译器警告发疯了?

【问题讨论】:

  • 我认为是 VS2010 中的代码分析给你这个警告,而不是编译器。

标签: c# dispose idisposable


【解决方案1】:

此警告的含义是,如果方法抛出(例如在 Subject 设置器中),您可能会留下一个未处理的 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 实例没有任何意见:)

【讨论】:

  • 谢天谢地,抱怨这个的不是编译器。
  • 我觉得用try {} finally {}或者using会更好
  • @ChrisWue:想法是只有在抛出异常时才处理message。
【解决方案2】:

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

为方便起见,为 DisposeWrapper 提供一个带有通用工厂方法的静态非泛型 DisposeWrapper 类可能会很方便,以允许编译器使用方法类型推断来推断包装类的类型。

因此可以做类似 [我习惯于 vb 语法,而不是 c#,所以如果这不太正确,请道歉):

{ using(var wrapper = DisposeWrapper.Create(new SomeDisposableThing)) { ... 用 wrapper.Value 做一些事情; 返回 wrapper.Keep(); } }

如果代码到达“return wrapper.Keep();”,那么 wrapper.Value 将被返回并且不会被释放。如果代码在没有调用 wrapper.Keep 的情况下退出 Using 语句,那么 wrapper.Value 将被释放。

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

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

【讨论】:

    猜你喜欢
    • 2011-05-25
    • 1970-01-01
    • 2016-03-22
    • 2017-10-27
    • 1970-01-01
    • 2011-03-08
    • 1970-01-01
    • 2012-04-19
    • 2011-01-20
    相关资源
    最近更新 更多