the other thing about "using" blocks

C#’s using statement is well recognized for one thing: calling dispose on objects so that you don’t have to. That bit is wonderful:

string contents;
using (var f = File.OpenText("/path/to/file") {
    contents = f.ReadToEnd();
}

This is much simpler than the fully spelled out alternative:

string contents;
var f = File.OpenText("/path/to/file");
try {
    contents = f.ReadToEnd();
} finally {
    f.Dispose();
}

And even this longer form actually misses one of the more interesting aspects of the using statement…

string contents;
var f = File.OpenText("/path/to/file");
try {
    contents = f.ReadToEnd();
} finally {
    f.Dispose();
}
f.ReadToEnd(); // oops! ObjectDisposedException at runtime
f = null; // you could set it to null
f.ReadToEnd(); // but now you have a NullReferenceException, even more mysterious

The using statement on the other hand, creates a scope, so its variable can’t be referenced at all after it is disposed:

string contents;
using (var f = File.OpenText("/path/ro/file") {
    contents = f.ReadToEnd();
}
f.ReadToEnd(); // unknown identifier error at compile-time

An entirely equivalent bit of code can be written, using an anonymous scope, but it starts to look quite baroque:

string contents;
{
    var f = File.OpenText("/path/ro/file");
    try {
        contents = f.ReadToEnd();
    } finally {
        f.Dispose();
    }
}
f.ReadToEnd(); // Unknown identifier at compile-time again, but 2x the lines and 2x the scopes!

using provides the try-finally-dispose structure, and also provides a scope. The scope means a whole class of errors where resources are accessed after being released is transformed from run-time to compile-time errors. Dealing with errors at compile-time is quicker, and with the right tools to highlight problems, the compile problems can be seen directly in the code as it is being edited.