Bet*_*moo 7 c# resources dispose destructor
我正在创建一个包含StreamWrite的简单类
class Logger
{
private StreamWriter sw;
private DateTime LastTime;
public Logger(string filename)
{
LastTime = DateTime.Now;
sw = new StreamWriter(filename);
}
public void Write(string s)
{
sw.WriteLine((DateTime.Now-LastTime).Ticks/10000+":"+ s);
LastTime = DateTime.Now;
}
public void Flush()
{
sw.Flush();
}
~Logger()
{
sw.Close();//Raises Exception!
}
}
Run Code Online (Sandbox Code Playgroud)
但是当我在析构函数中关闭此StreamWriter时,它引发了StreamWriter已被删除的异常?
为什么?以及如何使其工作,以便在删除Logger类时,StreamWriter在删除之前关闭?
谢谢!
Han*_*ant 16
在99.99%的情况下编写自己的析构函数(aka终结器)是错误的.需要它们来确保您的类释放一个操作系统资源,该资源不是由.NET框架自动管理的,并且未被您的类用户正确发布.
首先必须在您自己的代码中首先分配操作系统资源.这总是需要某种P/Invoke.这是非常很少用到,它是在微软工作,采取照顾的.NET程序员的工作.
他们是在StreamWriter的情况下做的.通过几个层,它是一个文件句柄的包装器,使用CreateFile()创建.创建句柄的类也是负责编写终结器的类.Microsoft代码,不是您的代码.
这样的类总是实现IDisposable,使类的用户有机会在完成资源时释放资源,而不是等待终结器完成工作.StreamWriter实现了IDisposable.
当然,您的StreamWriter对象是您的类的私有实现细节.您不知道用户何时完成Logger类,您无法自动调用StreamWriter.Dispose().你需要帮助.
通过自己实现IDisposable获得帮助.您的类的用户现在可以调用Dispose或使用using语句,就像她对任何框架类一样:
class Logger : IDisposable {
private StreamWriter sw;
public void Dispose() {
sw.Dispose(); // Or sw.Close(), same thing
}
// etc...
}
Run Code Online (Sandbox Code Playgroud)
嗯,这就是99.9%的情况下应该做的事情.但不是在这里.存在致命混淆的风险:如果实现IDisposable,则类的用户也必须有合理的机会调用其Dispose()方法.除了Logger类型类之外,这通常不是什么大问题.您的用户很可能希望在最后一刻记录某些内容.例如,她可以从AppDomain.UnhandledException记录未处理的异常.
在这种情况下何时调用Dispose()?您可以在程序终止时执行此操作.但是,除了这一点之外,如果在程序退出时发生资源,那么提前释放资源的意义不大.
记录器有特殊要求在所有情况下都能正常关闭.这要求您将StreamWriter.AutoFlush属性设置为true,以便在写入时立即刷新日志记录输出.鉴于难以正确调用Dispose(),现在实际上更好的是你没有实现IDisposable并让微软的终结器关闭文件句柄.
Fwiw,框架中有另一个类有同样的问题.Thread类使用四个操作系统句柄,但不实现IDisposable.调用Thread.Dispose()真的很尴尬,所以微软没有实现它.这会在非常特殊的情况下导致问题,您需要编写一个创建大量线程但从不使用new运算符创建类对象的程序.它已经完成了.
最后但同样重要的是:如上所述,编写记录器相当棘手.Log4net是一种流行的解决方案.NLog是一个更好的捕鼠器,一个感觉和工作的网络,而不是感觉像Java端口.
Destructors(aka Finalizers)不保证以特定顺序运行.查看文档:
两个对象的终结器不保证以任何特定顺序运行,即使一个对象引用另一个对象.也就是说,如果对象A具有对象B的引用并且两者都具有终结器,则当对象A的终结器开始时,对象B可能已经完成.
IDisposable而是实施.