In C#, if I want to deterministically clean up non-managed resources, I can use the "using" keyword. But for multiple dependent objects, this ends up nesting further and further:
using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
{
using (BufferedStream bs = new BufferedStream(fs))
{
using (StreamReader sr = new StreamReader(bs))
{
// use sr, and have everything cleaned up when done.
}
}
}
In C++, I'm used to being able to use destructors to do it like this:
{
FileStream fs("c:\file.txt", FileMode.Open);
BufferedStream bs(fs);
StreamReader sr(bs);
// use sr, and have everything cleaned up when done.
}
Is there a better way in C# to do this? Or am I stuck with the multiple levels of nesting?
You don't have to nest with multiple usings:
using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
// all three get disposed when you're done
}
In .NET Core, there's a new using statement which allows you to dispense with the parentheses, and the disposal happens at the end of the current scope:
void MyMethod()
{
using var fs = new FileStream("c:\file.txt", FileMode.Open);
using var bs = new BufferedStream(fs);
using var sr = new StreamReader(bs);
// all three are disposed at the end of the method
}
You can put using statements together before the opening braces like so:
using (StreamWriter w1 = File.CreateText("W1"))
using (StreamWriter w2 = File.CreateText("W2"))
{
// code here
}
http://blogs.msdn.com/ericgu/archive/2004/08/05/209267.aspx
You could use this syntax to condense things down a bit:
using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
}
This is one of those rare occasions where not using { } for all blocks makes sense IMHO.
I have implemented solutions like Michael Meadows's before, but his StreamWrapper code doesn't take into account if the Dispose() methods called on the member variables throw an exception for one reason or another, the subsequent Dispose()es will not be called and resources could dangle. The safer way for that one to work is:
var exceptions = new List<Exception>();
try
{
this.sr.Dispose();
}
catch (Exception ex)
{
exceptions.Add(ex);
}
try
{
this.bs.Dispose();
}
catch (Exception ex)
{
exceptions.Add(ex);
}
try
{
this.fs.Dispose();
}
catch (Exception ex)
{
exceptions.Add(ex);
}
if (exceptions.Count > 0)
{
throw new AggregateException(exceptions);
}
}
Instead of nesting using statements, you can just write out the .Dispose calls manually - but you'll almost certainly miss one at some point.
Either run FxCop or something else that can make sure that all IDisposable-implementing type instances have a .Dispose() call, or deal with the nesting.
you can omit the curly braces, like:
using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
// use sr, and have everything cleaned up when done.
}
or use the regular try finally approach:
FileStream fs = new FileStream("c:\file.txt", FileMode.Open);
BufferedStream bs = new BufferedStream(fs);
StreamReader sr = new StreamReader(bs);
try
{
// use sr, and have everything cleaned up when done.
}finally{
sr.Close(); // should be enough since you hand control to the reader
}
This makes for a much larger net plus in lines of code, but a tangible gain in readability:
using (StreamWrapper wrapper = new StreamWrapper("c:\file.txt", FileMode.Open))
{
// do stuff using wrapper.Reader
}
Where StreamWrapper is defined here:
private class StreamWrapper : IDisposable
{
private readonly FileStream fs;
private readonly BufferedStream bs;
private readonly StreamReader sr;
public StreamWrapper(string fileName, FileMode mode)
{
fs = new FileStream(fileName, mode);
bs = new BufferedStream(fs);
sr = new StreamReader(bs);
}
public StreamReader Reader
{
get { return sr; }
}
public void Dispose()
{
sr.Dispose();
bs.Dispose();
fs.Dispose();
}
}
With some effort, StreamWrapper could be refactored to be more generic and reusable.
It should be noted that generally when creating stream based off another stream the new stream will close the one being passed in. So, to further reduce your example:
using (Stream Reader sr = new StreamReader( new BufferedStream( new FileStream("c:\file.txt", FileMode.Open))))
{
// all three get disposed when you're done
}
for this example let us assume you have:
a file named 1.xml under c:\
a textbox named textBox1, with the multi-line properties set ON.
const string fname = #"c:\1.xml";
StreamReader sr=new StreamReader(new BufferedStream(new FileStream(fname,FileMode.Open,FileAccess.Read,FileShare.Delete)));
textBox1.Text = sr.ReadToEnd();
The using statement is syntactic sugar that converts to:
try
{
obj declaration
...
}
finally
{
obj.Dispose();
}
You can explicitly call Dispose on your objects, but it won't be as safe, since if one of them throws an exception, the resources won't be freed properly.
Related
In C# Dot Net, How to handle a exception when you want to de-serialize a xml file, but by default the file doesn't exists! because you have to run the program to create one.
Below is the area where I need Help.
public static Compare_Data[] Deserialize()
{
Compare_Data[] cm;
cm = null;
string path = #"C:\Users\XYZ\Desktop\BACKUP_DATA\log.xml";
XmlSerializer xs = new XmlSerializer(typeof(Compare_Data[]));
if (File.Exists(path))
{
using (FileStream fs = new FileStream(path, FileMode.Open))
{
// This will read the XML from the file and create the new instance of Compare_Data.
cm = (Compare_Data[])xs.Deserialize(fs);
return cm;
}
}
else
{
using (FileStream fs = new FileStream(path, FileMode.Create))
{
xs.Serialize(fs); /// what to add here ?
}
}
return null;
}
If general, you don't want your methods to have side effects. In this case, creating an empty log file in the else branch is probably unnecessary and should be handled by a separate Serialize() method when there is data to be logged.
Your code could be simplified something like this:
public static Compare_Data[] Deserialize()
{
const string path = #"C:\Users\XYZ\Desktop\BACKUP_DATA\log.xml";
if (!File.Exists(path))
{
// return null or an empty array, depending on how
// you want the calling code to handle this.
return null;
}
using (FileStream fs = new FileStream(path, FileMode.Open))
{
var xs = new XmlSerializer(typeof(Compare_Data[]));
return (Compare_Data[])xs.Deserialize(fs);
}
}
Given the following code snippet:
try
{
var myTxt = "";
var serializer = new DataContractSerializer(myObject.GetType());
var memoryStream = new MemoryStream()
serializer.WriteObject(memoryStream, myObject);
memoryStream.Position = 0;
using (var reader = new StreamReader(memoryStream))
{
myTxt = reader.ReadToEnd();
}
.
.
.
}
catch (IOException ioEx)
{
//log or whatever...
throw;
}
Typically I'd have a using statement around my memory stream,
but from my understanding disposing the StreamReader will close the Memory Stream.
So the question is, is there anything REALLY wrong with the above?
You could simplify your code as I don't see the point of writing, rewinding and reading:
var serializer = new DataContractSerializer(facets.GetType());
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, facets);
string xml = Encoding.UTF8.GetString(stream.ToArray());
}
I think its fine because MemoryStream doesn't hold unmanaged resources anyway, but if you wanted to be extra safe you could do this:
var memoryStream = new MemoryStream()
StreamReader reader = null;
try{
serializer.WriteObject(memoryStream, myObject);
memoryStream.Position = 0;
reader = new StreamReader(memoryStream)
//...
}
finally
{
if(reader != null)
reader.Dispose();
else
memoryStream.Dispose();
}
It is a good practice to use either
using (var stream = new MemoryStream(...))
using (var reader = new StreamReader(stream))
{
myTxt = reader.ReadToEnd();
}
Here both stream and reader will be disposed by runtime
or Close() manually both reader(writer) and stream
To test your scenario, I've added
memoryStream.Seek(0, SeekOrigin.Begin);
to the end and received an System.ObjectDisposedException, your code seems valid.
Of course vcsjones's comment is also valid.
im using a lot of streamwrtier in my application, and this one is suppsoed to log errors that happens while the program is running but at the moment it only creates the file withour writing into it :
if(File.Exists(currentLog))//currentLog = "Path\\Log.txt"
{
using (var fileStream = File.Open(currentLog,FileMode.Open))
{
StreamWriter ErreurStreamWriter = new StreamWriter(fileStream);
ErreurStreamWriter.WriteLine("{0}",e.Message);
ErreurStreamWriter.WriteLine("-------------------------------");
}
}
else
{
using (var fileStream = File.Create(currentLog))
{
StreamWriter ErreurStreamWriter = new StreamWriter(fileStream);
ErreurStreamWriter.Write("Liste des erreurs :");
ErreurStreamWriter.WriteLine("{0}", e.Message);
ErreurStreamWriter.WriteLine("-------------------------------");
}
}
Am I missing something obvious here ? Any help would be great, thanks
may be need to Flush() after write, or try to append when exist
Try this
using (var fileStream = File.Open(currentLog, FileMode.Open))
{
using (StreamWriter erreurStreamWriter = new StreamWriter(fileStream))
{
erreurStreamWriter.WriteLine("{0}", e.Message);
erreurStreamWriter.WriteLine("-------------------------------");
}
}
Basically what I think happens is that the stream writer never flush into the filestream.
I have some GZ compressed resources in my program and I need to be able to write them out to temporary files for use. I wrote the following function to write the files out and return true on success or false on failure. In addition, I've put a try/catch in there which shows a MessageBox in the event of an error:
private static bool extractCompressedResource(byte[] resource, string path)
{
try
{
using (MemoryStream ms = new MemoryStream(resource))
{
using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite))
{
using (GZipStream zs = new GZipStream(fs, CompressionMode.Decompress))
{
ms.CopyTo(zs); // Throws exception
zs.Close();
ms.Close();
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message); // Stream is not writeable
return false;
}
return true;
}
I've put a comment on the line which throws the exception. If I put a breakpoint on that line and take a look inside the GZipStream then I can see that it's not writeable (which is what's causing the problem).
Am I doing something wrong, or is this a limitation of the GZipStream class?
You are plumbing the pipes the wrong way. Fix:
using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite))
using (MemoryStream ms = new MemoryStream(resource))
using (GZipStream zs = new GZipStream(ms, CompressionMode.Decompress))
{
zs.CopyTo(fs);
}
I have a windows service writes its log in a text file in a simple format.
Now, I'm going to create a small application to read the service's log and shows both the existing log and the added one as live view.
The problem is that the service locks the text file for adding the new lines and at the same time the viewer application locks the file for reading.
The Service Code:
void WriteInLog(string logFilePath, data)
{
File.AppendAllText(logFilePath,
string.Format("{0} : {1}\r\n", DateTime.Now, data));
}
The viewer Code:
int index = 0;
private void Form1_Load(object sender, EventArgs e)
{
try
{
using (StreamReader sr = new StreamReader(logFilePath))
{
while (sr.Peek() >= 0) // reading the old data
{
AddLineToGrid(sr.ReadLine());
index++;
}
sr.Close();
}
timer1.Start();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void timer1_Tick(object sender, EventArgs e)
{
using (StreamReader sr = new StreamReader(logFilePath))
{
// skipping the old data, it has read in the Form1_Load event handler
for (int i = 0; i < index ; i++)
sr.ReadLine();
while (sr.Peek() >= 0) // reading the live data if exists
{
string str = sr.ReadLine();
if (str != null)
{
AddLineToGrid(str);
index++;
}
}
sr.Close();
}
}
Is there any problem in my code in reading and writing way?
How to solve the problem?
You need to make sure that both the service and the reader open the log file non-exclusively. Try this:
For the service - the writer in your example - use a FileStream instance created as follows:
var outStream = new FileStream(logfileName, FileMode.Open,
FileAccess.Write, FileShare.ReadWrite);
For the reader use the same but change the file access:
var inStream = new FileStream(logfileName, FileMode.Open,
FileAccess.Read, FileShare.ReadWrite);
Also, since FileStream implements IDisposable make sure that in both cases you consider using a using statement, for example for the writer:
using(var outStream = ...)
{
// using outStream here
...
}
Good luck!
Explicit set up the sharing mode while reading the text file.
using (FileStream fs = new FileStream(logFilePath,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite))
{
using (StreamReader sr = new StreamReader(fs))
{
while (sr.Peek() >= 0) // reading the old data
{
AddLineToGrid(sr.ReadLine());
index++;
}
}
}
new StreamReader(File.Open(logFilePath,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite))
-> this doesn't lock the file.
The problem is when you are writing to the log you are exclusively locking the file down so your StreamReader won't be allowed to open it at all.
You need to try open the file in readonly mode.
using (FileStream fs = new FileStream("myLogFile.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (StreamReader sr = new StreamReader(fs))
{
while (!fs.EndOfStream)
{
string line = fs.ReadLine();
// Your code here
}
}
}
I remember doing the same thing a couple of years ago. After some google queries i found this:
FileStream fs = new FileStream(#”c:\test.txt”,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite);
i.e. use the FileShare.ReadWrite attribute on FileStream().
(found on Balaji Ramesh's blog)
Have you tried copying the file, then reading it?
Just update the copy whenever big changes are made.
This method will help you to fastest read a text file and without locking it.
private string ReadFileAndFetchStringInSingleLine(string file)
{
StringBuilder sb;
try
{
sb = new StringBuilder();
using (FileStream fs = File.Open(file, FileMode.Open))
{
using (BufferedStream bs = new BufferedStream(fs))
{
using (StreamReader sr = new StreamReader(bs))
{
string str;
while ((str = sr.ReadLine()) != null)
{
sb.Append(str);
}
}
}
}
return sb.ToString();
}
catch (Exception ex)
{
return "";
}
}
Hope this method will help you.