Class destructor problem

I am making a simple class containing 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! } } 

But when I close this StreamWriter in the destructor, an exception occurs that the StreamWriter has already been deleted?

Why? And how to make it work so that when the Logger class is deleted, StreamWriter is closed before uninstalling?

Thanks!

+6
c # destructor resources dispose
source share
3 answers

Writing your own destructor (aka finalizer) is erroneous in 99.99% of all cases. They are necessary to ensure that your class releases an operating system resource that is not automatically managed by the .NET platform and is not correctly released by the user of your class.

This starts with the need to first allocate the operating system resource into its own code. It always requires a kind of P / Invoke. This is very rarely necessary, it is the work of .NET programmers working at Microsoft to take care of this.

They did this in the case of StreamWriter. Through several layers, this is a wrapper around the file descriptor created using CreateFile (). The class that created the handle is also the one responsible for writing the finalizer. Microsoft code, not yours.

Such a class always implements IDisposable, giving the user of the class the opportunity to release the resource when it is executed with it, instead of waiting for the finalizer to complete. StreamWriter implements IDisposable.

Of course, your StreamWriter object is a private implementation of your class. You do not know when the user has executed his Logger class, you cannot automatically call StreamWriter.Dispose (). You need help.

Get this help by implementing IDisposable yourself. The user of your class can now call Dispose or use the using statement, as with any of the framework classes:

  class Logger : IDisposable { private StreamWriter sw; public void Dispose() { sw.Dispose(); // Or sw.Close(), same thing } // etc... } 

Well, that is what you should do in 99.9% of all cases. But not here. At the risk of falsifying you: if you implement IDisposable, there should be a reasonable opportunity for the user of your class to call the Dispose () method. This is usually not a big problem, except for a class like Logger. It is likely that your user wants to register something until the last moment. So that she can register an unhandled exception from AppDomain.UnhandledException, for example.

When to call Dispose () in this case? You can do this when your program terminates. But, besides this, it makes no sense to allocate resources earlier if this happens when the program exits.

The registrar has a special requirement for proper closure in all cases. This requires that you set the StreamWriter.AutoFlush property to true so that the log output is reset as soon as it is recorded. Given the complexity of correctly calling the Dispose () function, it is now better if you do not implement IDisposable and do not allow the Microsoft finalizer to close the file descriptor.

Fwiw, there is another class within the framework that has the same problem. The Thread class uses four operating system descriptors, but does not implement IDisposable. Calling Thread.Dispose () is really inconvenient, so Microsoft did not execute it. This causes problems in very unusual cases, you need to write a program that creates many threads, but never uses a new operator to create class objects. That was done.

And last but not least, writing a logger is quite difficult, as explained above. Log4net is a popular solution. NLog is the best mousetrap, a library that feels and operates dotnetty, instead of feeling like a Java port.

+16
source share

Destructors (aka finalizers) are not guaranteed in a specific order. See the documentation :

Finalizers of two objects are not guaranteed to run in any particular order, even if one object refers to another. That is, if object A has a reference to object B, and both have finalizers, object B can already be finalized when the finalizer of object A begins.

Pip IDisposable .

+5
source share

Generally, when it comes to implementing Finalizer, do not. Usually they are only required if your class uses unmanaged memory directly. If you are running Finalizer, it should never reference any managed members of the class, since they can no longer be valid references.

As an additional caution, keep in mind that Finalizer runs in its own thread, which can overturn you if you use unmanaged APIs that have thread-like. These scripts are much cleaner with IDisposable and nice, orderly cleanup.

+2
source share

All Articles