Filestreams

  • Thread starter Thread starter Bob Weiner
  • Start date Start date
B

Bob Weiner

Hi,

I'm an IT guy who likes to think he can program. I have a generic question
about filestreams.

I'm putting together an application and need to write certain activities out
to a log file. I created a Log class to manage all file operations.

The class has Open, Close, and Record methods. I don't understand how the
FileStream object functions, though. If I create the object with FileMode =
OpenOrCreate and FileAccess = ReadWrite it seems I should be able to
attach/detach a StreamWriter object as necessary.

In fact, it works fine until I close the StreamWriter. When I reopen I find
the the FileStream is no longer writeable.

Is there a simple explanation to correct my misunderstanding of the
FileStream or a good tutorial on how they work that I should read. Forgive
me for not searching more on my own but I'm late for a meeting and I have to
get this code out the door asap.

Thanks, bob
 
Hi,

Closing the StreamWriter closes the underlying Stream, in your case the
FileStream and so the file.
You could keep the StreamWriter open as long as you keep the FileStream
open.
Or you could not close the StreamWriter at all. I think, this is an
acceptable exception from the rule, that any IDisposable should be disposed
after use.

Greetings
Christof
 
By default, StreamWriter assumes ownership of the stream and calls
Dispose / Close on your behalf. Personally I don't like this, and
prefer the "bool leaveOpen" approach used in the compression classes
to allow either usage. There is a ctor for non-closing writers, but it
is internal. Jon Skeet has a non-closing stream wrapper, but since you
are leaving the stream open, why not just reference the writer instead
of the stream? a writer will still allow the same binary access, while
providing text access. Neither approach will make the file any more
accessible.

Marc
 
Bob,

When you close the writer it closes the underlying stream that's why it is
no longer writable. My suggestion is to keep and reuse the writer untill you
are done writing; otherwise you need to reopen the file everytime you need
to write.
 
(my binary comment is incorrect, unless you talk to BaseStream which
could get scrappy with cache/flusing - don't do this).

Agree with Christof - in this case we have (without option) delegated
disposal accountability, so it is a bit of an exception to the "using"
pattern, so long as your writer is itself closed and disposed at some
point.

Marc
 
Bob said:
I'm an IT guy who likes to think he can program. I have a generic question
about filestreams.

I'm putting together an application and need to write certain activities out
to a log file. I created a Log class to manage all file operations.

The class has Open, Close, and Record methods. I don't understand how the
FileStream object functions, though. If I create the object with FileMode =
OpenOrCreate and FileAccess = ReadWrite it seems I should be able to
attach/detach a StreamWriter object as necessary.

In fact, it works fine until I close the StreamWriter. When I reopen I find
the the FileStream is no longer writeable.

StreamWriter and friends (TextReader and TextWriter descendants) close
the inner stream (the stream they were passed in the constructor) when
they themselves are closed. They do this to compose well with idioms
like this:

using (TextWriter writer = new StreamWriter(File.Open('foo.txt')))
//...

If I were writing a logging class, BTW, I'd be sure to open and close
the file in the logging method, in case of someone forgetting to dispose
the logger and thus close the file (which would lose all the log
messages). Also, it wouldn't hurt to have some synchronization (if
that's necessary; I don't know your details).

E.g.

public class Logger
{
string _logFile;
object _lock = new object();

public Logger(string logFile)
{
_logFile = logFile; // null check etc.
}

public void Log(string message)
{
lock (_lock) // assuming this is the only logger writing to this
// file
using (TextWriter writer = File.AppendText(_logFile))
writer.WriteLine(message);
}
}

There are many other concerns that can come into writing logging
architectures though, so using a prebuilt architecture / module may be
worthwhile.

-- Barry
 
Thanks, great comments!

My program is interactive and will archive (but not delete) batches of users
from an Active Directory. Users will have their accounts disabled, moved to
a special OU, and added to a mail-enabled group (disabled users with mail
forwarding enabled will still recieve mail).

In its current form, the User class (with the archive method) and the Group
class will be the only two that use the Log class. These will run
sequentially in one thread so there is no danger of both trying to write to
the same stream simultaneously. The path to the log file will be a
read-only, static variable in the Log class so there is only one log file.
For now I can just leave the stream open and let it dispose of itself at the
end of all things.

The fact that there is a single stream composed of multilple objects which
was closed by the writer is an important point I didn't know. Also because
a single batch may contain hundreds of users, I may update it to a
mutli-threaded version; I guess that will make locking important.

Seems like I/O should be simple!

thanks all, bob
 
For now I can just leave the stream open and let it dispose of
itself at the end of all things.

I wouldn't trust this approach; firstly, objects *never" dispose
themselves; they may get finalized by the GC, but that is not (quite)
the same thing. In fact, finalizers aren't fully guaranteed to run at
all, let alone at a predictable time. My advice: find a point in your
program where you know you are preparing to exit; explicitely Close()
and Dispose() the stream/writer, presumably by a static Close() method
on the Log class. The other option is to look for existing logging
solutions; they may do everything you need (and more) and abstract
away the complexities, including (possibly) tricks like queueing
writes in a producer/consumer pattern to avoid being IO-bound (just
blocked while enqueing).

In the threaded scenario, yes: you will need locking.

Marc
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top