Introduction and Background
So, it all began on the Facebook when I was posting a status (using Twitter) about a statement that I was feeling very bad that .NET team had left out the “Close” function while designing their .NET Core framework. At first I thought maybe everything was “managed” underground until I came up to an exception telling me that the file was already being used. The code that I was using was something like this,
if(!File.Exists("path")) { File.Create("path").Close(); }
However, “Close” was not defined and I double checked against .NET Core reference documentations too, and they suggested that this was not available in .NET Core framework. So I had to use other end… Long story short, it all went up like this,
I am unable to understand why was “Close” function removed from FileStream. It can be handy guys, @dotnet.
Then, Vincent came up with the idea of saying that “Close” was removed so that we all can use “using” blocks, which are much better in many cases.
You don’t need that. It’s handier if you just put any resource that eats resources within the “using block”. 🙂
Me:
How does using, “using (var obj = File.Create()) { }” make the code handier?
I was talking about this, “File.Create().Close();”
Now, instead of this, we have to flush the stream, or perform a flush etc. But I really do love the “using block” suggestion, I try to use it as much as I can. Sometimes, that doesn’t play fair. 😉
He:
Handier because I don’t have to explicitly call out the “Close()” function. Depends on the developer’s preference, but to me, I find “using (var obj = File.Create()) { }” handier and sexier to look at rather than the plain and flat “File.Create().Close();”. Also, it’s a best practice to “always” use the using block when dealing with objects that implements IDisposable to ensure that objects are properly closed and disposed when you’re done with it. 😉
As soon as you leave the using block’s scope, the stream is closed and disposed. The using block calls the Close() function under the hood, and the Close() calls the Flush(), so you should not need to call it manually.
Me:
I will go with LINQPad to see which one is better. Will let you know.
So, now I am here, and I am going to share what I find in the LINQPad. The fact is that I have always had faith in the code that works fast and provides a better performance. He is an ASP.NET MVP on Microsoft and can be forgiven for the fact that web developers are trained on multi-core CPUs and multi-deca-giga-bytes of RAMs so they use the code that looks cleaner but… He missed the C# bytecode factor here. I am going to use LINQPad to find out the underlying modifications that can be done to find out a few things.
Special thanks to Vincent: Since a few days I was out of topics to write on, Vincent you gave me one and I am going to write on top of that debate that we had.
Notice: I also prefer using the “using” block in almost every case. Looks handier, but the following code block doesn’t look handier at all,
using (var obj = File.Create("path")) { }
And this is how it began…
Exploring the “using” and simple “Close” calls
LINQPad is a great software to try out the C# (or .NET framework) code and see how it works natively, it lets you see the native bytecode of ,NET framework and also lets you perform and check the tree diagrams of the code. The two types that we are interested in are, “using” statement of .NET framework and the simple “Close” calls that are made to the objects to close their streams.
I used a Stopwatch object to calculate the time taken by the program to execute each task, then I match the results of each of the program with each other to find out which one went fast. Looking at the code of them both, it looks the thousand-feet high view of them both looks the same,
// The using block using (var obj = File.Create("path")) { } // The clock method File.Create("path").Close();
They both the same, however, their intermediate code shows something else.
// IL Code for "using" block IL_0000: nop IL_0001: ldstr "F:/File.txt" IL_0006: call System.IO.File.Create IL_000B: stloc.0 // obj IL_000C: nop IL_000D: nop IL_000E: leave.s IL_001B IL_0010: ldloc.0 // obj IL_0011: brfalse.s IL_001A IL_0013: ldloc.0 // obj IL_0014: callvirt System.IDisposable.Dispose IL_0019: nop IL_001A: endfinally IL_001B: ret // IL Code for the close function IL_0000: nop IL_0001: ldstr "F:/File.txt" IL_0006: call System.IO.File.Create IL_000B: callvirt System.IO.Stream.Close IL_0010: nop IL_0011: ret
Oh-my-might-no! There is no way, “using” block could have ever won with all of that extra intermediate code for the .NET VM to execute before exiting. The time taken by these commands was also tested and for that I used the native Stopwatch object to calculate the “ticks” used, instead of the time in milliseconds by each of the call. So my code in the LINQPad looked like this,
void Main() { Stopwatch watch = new Stopwatch(); watch.Start(); using (var obj = File.Create("F:/file.txt")) { } watch.Stop(); Console.WriteLine($"Time required for 'using' was {watch.ElapsedTicks}."); watch.Reset(); watch.Start(); File.Create("F:/file.txt").Close(); watch.Stop(); Console.WriteLine($"Time required for 'close' was {watch.ElapsedTicks}."); }
The execution of the above program always results in a win for the “Close” function call. In sometimes it was a close result, but still “Close” function had a win over the “using” statement. The results are shown the images below,
The same executed, produced different results, there are many factors to this and it can be forgiven for all of them,
- Operating system might put a break to the program for its own processing.
- Program might not be ready for processing.
- Etc. etc. etc.
There are many reasons. For example, have a look at the last image, the time taken was 609 ticks. Which also includes the ticks taken by other programs. The stopwatch was running ever since, and that is what caused the stopwatch to keep tracking the ticks. But in any case, “using” statement was not a better solution from a low level code side.
Final words
Although I also recommend using the “using” block statement in C# programs, but there is a condition where you should consider using one over other. For example, in this condition, we see that implementing one over the other has no benefit at all, but just a matter of personal interest and like. Although I totally agree to Vincent where he mentions that “using” block is helpful, but in other cases. In this case, adding a Close call is a cleaner (IMO) way of writing the program as compared to the other one.
At the end, it is all in the hands of the writer… Select the one you prefer.