【问题标题】:When is StreamWriter disposed?StreamWriter 什么时候处理?
【发布时间】:2018-11-21 19:55:51
【问题描述】:

我有一个小班,我拼凑在一起来实现一个快速记录器。我用在构造函数中实例化的私有System.IO.StreamWriter 组成它。因为我使用的方式是阻止我实现using 块,所以我添加了一个终结器并在其中调用StreamWriter 的Dispose() 方法。但是,在执行时,该终结器会引发异常,因为 StreamWriter 已被释放。

System.ObjectDisposedException - 无法访问已关闭的文件。

我很困惑这是怎么发生的,我想知道这是否意味着我不需要担心清理 StreamWriter。这是我的课:

public class TextFileLogger : AbstractTextLogger
{
    private const string LogPath = @"C:\";
    private const string LogFileName = "MyFile.log.txt";
    private readonly StreamWriter _writer;

    public TextFileLogger()
    {
        _writer = new StreamWriter($"{LogPath}{LogFileName}", true);
        _writer.AutoFlush = true;
    }

    ~TextFileLogger()
    {
        _writer.Dispose();
    }

    protected override void WriteLine(string line)
    {
        _writer.WriteLine(line);
    }
}

【问题讨论】:

  • 你不能那样做。终结器(又名析构函数)在单独的线程上运行,不保证您的对象所引用的其他托管对象仍然存在。您应该在终结器中清理的唯一内容是非托管资源。使您的 TextFileLogger Disposable(实现 IDisposable)并阅读“Dispose Pattern”以使其正确。如果一个类没有原生资源(如你的),它不应该有终结器
  • @Flydog57 - 我熟悉 IDisposable 的实现,并且之前做过几次。我想我可以以某种方式在我的类中处理 StreamWriter 而不需要 using 块。由于这是一个快速而肮脏的临时事情,我可能只需要在每次调用 WriteLine 时更新 StreamWriter,以便我可以在 using 块中使用它。这太糟糕了,根据我的测量,执行时间会长约 14.5 倍,但对于一个短暂的项目,我宁愿承受性能损失,也不愿完全重新架构所有内容。
  • 为代码中的其他地方添加一个 Dispose 方法。只要它在您的正常代码中,您就可以将其丢弃。然而,一旦你进入终结器代码,所有的“托管代码保证”大部分都会从桌面上消失。终结器很容易搞砸。

标签: c# .net idisposable finalizer


【解决方案1】:

您可以在终结器中访问的唯一内容是根对象(如静态变量)或派生自 CriticalFinalizerObject 的对象。

问题是终结器不是确定性的,它可以以任何感觉的顺序运行。您遇到的问题是因为文本编写器在您上课之前完成。

您需要要么只是“希望最好”并让编写器的终结器来完成工作,要么您需要重构您的代码,以便您的类本身是可处置的,并调用流编写器的 dispose 方法。

【讨论】:

  • 如果我只是删除终结器并保持其余部分不变,我的班级会做什么?我对您的回答和建议我在课堂上实施 IDisposable 的 cmets 感到有些惊讶。我熟悉这个概念,并且以前也这样做过,但我认为有一些方法可以简单地调用 IDisposable 的 Dispose 方法,而无需使用块来触发它。我希望这个类有一个“has-a”而不是“is-a”方法,并且在这里实现 IDisposable 可能不是一个选项(消费代码也不能使用 using 块)。
  • 是的,删除终结器将是“最好的希望”选项。
  • 我决定只实现 IDisposable,而不是包含带有布尔处理参数的虚拟方法和对 GC.SuppressFinalizer 的调用的模式。这个项目不值得为此创建一个完整的基类层次结构。相反,我只是从我的类的 Dispose() 方法中调用 StreamWriter 的 Dispose() 方法,然后我在 using 块中找到了一个使用我的类的地方(这有点 hacky,但它有效)。这基本上就像这个公认的答案:stackoverflow.com/questions/10038321/…
  • 你对那个简单的版本有什么想法吗(在我之前的评论链接中)?我不确定为什么唯一能找到的建议是使用公共和虚拟保护方法实现整个 IDisposable 基类,并调用 GC.SupressFinalize()。看起来简单的版本应该可以完成这项工作,但我想它有问题,因为您从未在文档或建议中看到它。我想我仍然只是希望最好的。 ;)
  • 我认为有一个带有布尔值的虚拟方法的建议是一个糟糕的模式,恕我直言。这是一篇很好的文章,它解释了一个更好的模式,您将永远不需要再次检查该 bool codeproject.com/Articles/29534/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-01
  • 2010-12-27
相关资源
最近更新 更多