![]() |
Languages »
C# »
General
Intermediate
License: The Code Project Open License (CPOL)
Using Keyword Can Cause BugsBy Paulo ZemekThis article shows why not even the "using" keyword is a failsafe mechanism |
C#, .NET, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
C# is a safe and managed language. By safe, we can understand it will help developers avoid common errors like causing memory leaks. This really works, but for some developers, this is not in the best way. For example, in C++, I wrote:
AutoTransaction transaction;
... some code here...
transaction.Commit();
And, if some exception was thrown, the AutoTransaction destructor will be called immediately.
To start, I can't do it the same way in C#, so I will use the first "natural" equivalent:
AutoTransaction transaction = new AutoTransaction();
... some code here...
transaction.Commit();
I could use var instead of the first AutoTransaction declaration, but that's not the problem. If an exception is thrown, the "destructor" will not be called immediately. It will be called, but not at this moment. So, I consider this a bug in my code, as it can cause many time-outs in the database. So, I would need to write something like this:
using(AutoTransaction transaction = new AutoTransaction())
{
... some code here...
transaction.Commit();
}
This way, the compiler will generate the well known structure:
AutoTransaction transaction = new AutoTransaction();
try
{
... code here...
}
finally
{
// in theory there is an if (transaction != null) here,
// but I think optimization will remove it.
transaction.Dispose();
}
Using keyword is really a good shortcut. If you try this, you will notice that Dispose() will be called freeing the transaction immediately... most of the time.
And so, there is the real problem. I already did some interview tests with only invalid questions, like:
using (in general, this is the right answer, even not being the real one). new/try/finally Dispose. Also, considered the right one. null. In try, you alloc it, in finally, you test if it is different than null and, if it is, call Dispose. And, what you think? This is also considered right in some tests. But, none is correct. Imagine a very unfair and competitive world.
AutoTransaction transaction = new AutoTransaction();
// another thread causes an abort HERE!
try
But, I spoke about an alternative. Declaring the variable with null and allocating it in try. So, the code:
AutoTransaction transaction = null;
try
{
transaction = new AutoTransaction();
}
finally
{
if (transaction != null)
transaction.Dispose();
}
Now, you say:
Abort() appears just before the try, there is no problem (I agree); Abort() appears just before the transaction = new AutoTransaction(); there is no problem (and I agree again); Abort appears just after the transaction = new AutoTransaction(); there is no problem (and I agree again); So, there is no problem (and I disagree).
Why?
Because, even being in the same line, the transaction = is one command and the new AutoTransaction is another command.
The Abort() can happen just in the middle. So, the AutoTransaction is created, its pointer is not set to the variable, the Abort() happens and the finally does nothing.
Well, at this point, we see that using keyword or the try/finally blocks are useless... except for the fact that finally runs. So, to solve our problem, our allocation must also be in the finally block.
So:
AutoTransaction transaction = null;
try
{
try
{
}
finally
{
transaction = new AutoTransaction();
}
}
finally
{
if (transaction != null)
transaction.Dispose();
}
This will work... but, of course, the AutoTransaction has nothing of "Auto" in it, as it had in C++. Of course, in C++ you couldn't abort threads, as this could cause memory leaks. But, the difference, as C++ did always say you could not abort threads, is that there it was not used. But, in C#, or better, in the ASP.NET world, IIS uses it a lot... and no one guarantees you would be in the right place. To make it worse, imagine you are using the "using" keyword to make unsafe calls safe. You just caused a great and a hard to find bug, hard to happen (but that will happen) bug.
In the attached files, a sample that will create a file and dispose it. But, using the "using" keyword and the try block I presented here, the "file is being used by another process" exception is always caused by the "using" keyword.
I hope this makes web-developers think again when using the "using" keyword to make "sure" everything is ok and cleaned.
General
News
Question
Answer
Joke
Rant
Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads.
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 27 Nov 2009 Editor: Deeksha Shenoy |
Copyright 2009 by Paulo Zemek Everything else Copyright © CodeProject, 1999-2010 Web21 | Advertise on the Code Project |