0

I have a singleton-like class that can do some logging output:

class Foo
{
    private static Foo instance;
    private System.IO.StreamWriter os;

    private Foo()
    {
        this.os = System.IO.File.CreateText( "D:/tmp/test" );
    }

    public static Foo Instance
    {
        get
        {
            if ( instance == null )
                instance = new Foo();
            return instance;
        }
    }

    public void Log( string s )
    {
        os.Write( s );
    }
}

When I use that in a sample program like

class Program
{
    private static void Main( string[] args )
    {
        Foo.Instance.Log( "asdf\n" );
    }
}

the file is being created, but no output is written. I assume this is because the StreamWriter has never been flushed.

I tried to repair the class by calling to Close() in ~Foo():

~Foo()
{
    os.Close();
}

but this yields a ObjectDisposedException. Apparently Foo.os has already been disposed when Foo's destructor is called.

How do I ensure that my StreamWriter is flushed "at last"?

EDIT

Setting this.os.AutoFlush = true; works. Adding a Flush() method to Foo and calling it in appropiate places does as well, but I'm interested if there any way of doing without.

mkluwe
  • 3,823
  • 2
  • 28
  • 45
  • Implement the [IDisposable interface](https://msdn.microsoft.com/en-us/library/system.idisposable(v=vs.110).aspx) – Steve Apr 08 '16 at 13:37
  • I thought of that, but in the given example `Dispose()` won't get called. – mkluwe Apr 08 '16 at 13:45
  • That's a destructor. It is different See:http://stackoverflow.com/questions/339063/what-is-the-difference-between-using-idisposable-vs-a-destructor-in-c – Steve Apr 08 '16 at 13:51
  • I know about the difference between a destructor and `Dispose()`. What I wanted to say is that in the given example the only instance of `Foo()` is just garbage collected. If I implement `IDisposable`, this won't be of much use, as the garbage collector does not call `Dispose()`. – mkluwe Apr 08 '16 at 13:55
  • You'll have to look for another explanation. Crystal ball says that you look at the wrong file or look at it before the program has terminated. The latter can be changed by setting the AutoFlush property to true but it is fairly expensive. – Hans Passant Apr 08 '16 at 13:57
  • After some research, it seems turning on AutoFlush is actually good option for logging. Reference: http://stackoverflow.com/a/2530147/1878585 – Thariq Nugrohotomo Apr 08 '16 at 14:07
  • @Hans Uh-oh. I did my best to simplify the problem to this 30 line example. Did you run it? *Please*, do not say that you get a file containing `asdf` on your system? Is a `StreamWriter` supposed to "self-flush" at its end? – mkluwe Apr 08 '16 at 14:07
  • @ThariqNugrohotomo Yes, using `AutoFlush` works, thank you. Added that information in an edit. – mkluwe Apr 08 '16 at 14:31

2 Answers2

0

You can use StreamWriter which does have a Flush method.

There is another option for what you are trying to accomplish, you can use File.AppendAllText and will work. This way the StreamWriter is not open all the time.

class Foo
{
    private static Foo instance;
    private System.IO.StreamWriter os;

    private Foo()
    {
        this.os = new System.IO.StreamWriter("D:/tmp/test.txt");
    }

    public static Foo Instance
    {
        get
        {
            if (instance == null)
                instance = new Foo();
            return instance;
        }
    }

    public void Log(string s)
    {
        os.WriteLine(s);
        os.Flush();
    }

    public void Log2(string s)
    {
        System.IO.File.AppendAllText(@"D:/tmp/test2.txt",s);
   }
}
0

First of all, using a singleton creates problems in its own right, and this did not need another proof. Here, it's cleanup for a disguised global. The StreamWriter does not auto-flush on program end and according to the documentation,

You must call Close to ensure that all data is correctly written out to the underlying stream.

Thanks to an answer to "Self-closing StreamWriter singleton" from @PeterDuniho a possible solution could be changing the constructor to

private Foo()
{
    this.os = System.IO.File.CreateText( "D:/tmp/test" );
    System.AppDomain.CurrentDomain.ProcessExit +=
        (sender, eventArgs) => this.os.Close();
}

Considering the problem of calling Close() in the destructor, I should not have ignored the "finalizers are not of much use anyway" written all over the place. In this case, as garbage collection does not use a specific order, the StreamWriter object has already been collected and cannot be closed in its resurrected zombie state.

Community
  • 1
  • 1
mkluwe
  • 3,823
  • 2
  • 28
  • 45