我有以下类似记录器的类模式:
public class DisposableClassWithStream : IDisposable
{
public DisposableClassWithStream()
{
stream = new FileStream("/tmp/file", FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
writer = new StreamWriter(stream);
}
public void WriteLine(string s)
{
writer.WriteLine(s);
}
~DisposableClassWithStream()
{
Dispose();
}
private readonly object disposableLock = new object();
private bool isDisposed;
public void Dispose()
{
lock (disposableLock)
{
if (!isDisposed)
{
writer.Close();
stream.Close();
GC.SuppressFinalize(this);
isDisposed = true;
}
}
}
private FileStream stream;
private TextWriter writer;
}
以及使用此类的非常简单的代码:
public static void Main(string[] args)
{
var t = new DisposableClassWithStream();
t.WriteLine("Test");
}
代码在 .net 和 mono 上抛出(非确定性)ObjectDisposedException
是由对象 writer
的方法 Close
引起的,因为它试图将缓冲区刷新到已处理的 stream
。
我理解的原因是GC
在writer
之前完成了stream
。如何更改类模式以确保 stream
不会在 writer
之前被释放?
我厌倦了在构造函数中使用 GC.SuppressFinalize(writer)
,但我不确定它是不是太 hacky 了。
编辑:
我想首先解决终结器的问题。如问题开头所述,该类用作记录器,我想确保在关闭进程之前将来自 writer
的所有行都刷新到硬盘。
最佳答案
不要创建终结器,除非您的IDisposable
实现确实适用于非托管资源。 FileStream
和 StreamWriter
是托管资源。
此外,由于SafeHandle
引入后,很难想象用例,当任何非托管资源无法包装到托管 SafeHandle
中时。不要盲目地遵循 MSDN 中的 IDisposable
实现 - 这没关系,但它适用于您的类型同时操作托管和非托管资源的情况。
完全删除终结器,并从 Dispose
中丢弃 GC.SuppressFinalize(this);
:
public void Dispose()
{
lock (disposableLock)
{
if (!isDisposed)
{
writer.Close();
stream.Close();
isDisposed = true;
}
}
}
更新。
终结器用于非托管资源清理。
您可以将终结器视为关闭文件句柄、网络套接字等的地方。这不是任何应用程序逻辑的地方。通常,托管对象在终结期间处于不可用状态 - 无法保证其任何 IDisposable
(如 stream
或 writer
在您的示例)尚未最终确定。
如果您想确定该特定日志消息是否已刷新到文件中,则写入它并调用 writer.Flush()
。否则,如果您不希望立即刷新,请确保您在应用程序关闭时为记录器调用 dispose。另请注意,您无法防止进程终止,因此不要对记录器过于偏执。
关于c# - 抑制 .NET 中流的过早终结,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24279621/