I've just finished reading "C# 4.0 in a Nutshell" (O'Reilly) and I think it's a great book for a programmer willing to switch to C#, but it left me wondering. My problem is the definition of using
statement. According to the book (p. 138),
using (StreamReader reader = File.OpenText("file.txt")) { ... }
is precisely equivalent to:
StreamReader reader = File.OpenText("file.txt"); try { ... } finally { if (reader != null) ((IDisposable)reader).Dispose(); }
Suppose, however, that this is true and that this code is executed in a separate thread. This thread is now aborted with thread.Abort()
, so a ThreadAbortException
is thrown and suppose the thread is exactly after initializing the reader and before entering the try..finally
clause. This would mean that the reader is not disposed!
A possible solution would be to code this way:
StreamReader reader = null; try { reader = File.OpenText("file.txt"); ... } finally { if (reader != null) ((IDisposable)reader).Dispose(); }
This would be abort-safe.
Now for my questions:
using
statement is not abort-safe or are they wrong and it behaves like in my second solution?using
is equivalent to the first variant (not abort-safe), why does it check for null
in finally
?ThreadAbortException
can be thrown anywhere in managed code. But maybe there are exceptions and the first variant is abort-safe after all?EDIT: I know that using thread.Abort()
is not considered good practice. My interest is purely theoretical: how does the using
statement behave exactly?
C is an imperative procedural language supporting structured programming, lexical variable scope, and recursion, with a static type system. It was designed to be compiled to provide low-level access to memory and language constructs that map efficiently to machine instructions, all with minimal runtime support.
C-- (pronounced C minus minus) is a C-like programming language. Its creators, functional programming researchers Simon Peyton Jones and Norman Ramsey, designed it to be generated mainly by compilers for very high-level languages rather than written by human programmers.
While both C and C++ may sound similar, their features and usage are different. C is a procedural programming language and does not support objects and classes. C++ is an enhanced version of C programming with object-oriented programming support.
The book's companion web site has more info on aborting threads here.
In short, the first translation is correct (you can tell by looking at the IL).
The answer to your second question is that there may be scenarios where the variable can be legitimately null. For instance, GetFoo() may return null here, in which you wouldn't want a NullReferenceException thrown in the implicit finally block:
using (var x = GetFoo()) { ... }
To answer your third question, the only way to make Abort safe (if you're calling Framework code) is to tear down the AppDomain afterward. This is actually a practical solution in many cases (it's exactly what LINQPad does whenever you cancel a running query).
There's really no difference between your two scenarios -- in the second, the ThreadAbort could still happen after the call to OpenText, but before the result is assigned to the reader.
Basically, all bets are off when you get a ThreadAbortException. That's why you should never purposely abort threads rather than using some other method of gracefully bringing the thread to a close.
In response to your edit -- I would point out again that your two scenarios are actually identical. The 'reader' variable will be null unless the File.OpenText call successfully completes and returns a value, so there's no difference between writing the code out the first way vs. the second.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With