Question

I understand that any time I am instantiating a class that implements IDisposable, I should use the using keyword in order to ensure that it's disposed of properly.

Like so:

using (SecureString s = new SecureString())
{

}

The above is easy for me to understand - I can use s however I want within those brackets, but once I leave those brackets, I can no longer refer to s. The scope is easy to see.

But what I don't understand is how it works when you use using with no enclosing brackets.

private void Function()
{
    // Some code here

    using (SecureString s = new SecureString())

    // more code here
}

You aren't required to use brackets at all... so... how do I know where I am able to use the object and where it gets disposed, if there are no brackets to go with the using keyword?

Was it helpful?

Solution

In almost every case in C# when you have the choice of using braces you can substitute them with a single line of code.

Consider an if statement:

if (someBool)
    DoSomething();
else
    DoSomethingElse();

That is just as valid as the following:

if (someBool)
{
    // do lots of things
}
else
    DoSomethingElse();

This is probably almost universally true for any time you can use the { and } brackets.

The nice thing about this with the using statement is that you can nest them like this:

using (var stream = new NetworkStream(...))
using (var sslStream = new SslStream(stream))
using (var reader = new StreamReader(sslStream))
{
    reader.WriteLine(...);
}

This is equivalent to the following:

using (var stream = new NetworkStream(...))
{
    using (var sslStream = new SslStream(stream))
    {
        using (var reader = new StreamReader(sslStream))
        {
            reader.WriteLine(...);
        }
    }
}

Although I think you'd agree it's much nicer looking.

OTHER TIPS

A using without braces means the using is in the scope of the next statement only - the same way the if condition works.

using (SecureString s = new SecureString())
    s.Foo(); // Works
s.Foo(); // out of scope

As a personal preference, I always include the braces, even for single statement if/using constructs, to avoid confusing situations like this.

Then it is valid ony in the next command after the using statement.

Please note that it does not mean next line. Only next command.

The scope is essentially limited to the very next line.

using(var s = new SecureString())
    s.Whatever();
s.Whatever() // ERROR! s is out of scope

It's the same as using the if keyword with no brackets:

if(x == y)
    x = 4;

With statements like using, if, and foreach, if you do not include brackets, the scope is simply the next statement (usually the next line). It's treated just like you included the brackets around that next statement. E.g.

if (someCondition)
    Console.WriteLine("someCondition is true!");
Console.WriteLine("I run regardless");
// same as
if (someCondition)
{
    Console.WriteLine("someCondition is true!");
}

using (var s = new SecureString())
    Console.WriteLine(s); // this works!
//Console.WriteLine(s); // s is no longer in scope, this won't compile

As everyone has answered this in that that you can essentially have an inline using statement that is equivelent to this:

using(var s = new SecureString()) s.Whatever();

However please don't do this as you will in my opinion make the code less readable and confuse other developers. I think this point is well made by the fact that you asked this question.

To expand on other answers:

using (SecureString s = new SecureString())
    // This statement is in using scope.

// This is not.

If there are no brackets, you just get one line. Same with "if" or "for" statements. For example:

for (int i = 0; i < 10; i++)
    Console.WriteLine(i.ToString());

if (str == "hello")
    Console.WriteLine("hello back!");

using (SecureString s = new SecureString())
    Console.WriteLine("Here we have s");

Console.WriteLine("but here it's out of scope already");

If you do not use brackets, only the next statement is valid

using (SecureString s = new SecureString())
    // statement here

If you want to include more than one statement, use brackets.

using, just like for, while, if, else, and so many other keywords, always take exactly one statement as the "body" of the block.

If a programmer would like to execute multiple statements within the body they can use curly braces to create a complex statement, that is a single statement that executes multiple (or zero) statements. When you use curly braces after a using or if you're saying, "everything in here should be treated as one statement". If you already had exactly one statement to begin with there is no need to use braces to turn that into a single statement, so you can omit them.

Looking at a disassembly will show you exactly what is going on.

  static private void Function()
  {
     Console.WriteLine("before");

     using (SecureString s = new SecureString())
        s.Clear();

     Console.WriteLine("after");
  }

Using IL Dissassembler, the function above disassembles to the code below. You can see that the using statement is implemented with a try-finally wrapping the rest of the statement (which could be a block defined by curly braces, but in this case is just to the semicolon). The object you are using is disposed in the finally block. Then, outside the finally, the code continues with the next statement from the source.

.method private hidebysig static void  Function() cil managed
{
  // Code size       56 (0x38)
  .maxstack  2
  .locals init ([0] class [mscorlib]System.Security.SecureString s,
           [1] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldstr      "before"
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000b:  nop
  IL_000c:  newobj     instance void [mscorlib]System.Security.SecureString::.ctor()
  IL_0011:  stloc.0
  .try
  {
    IL_0012:  ldloc.0
    IL_0013:  callvirt   instance void [mscorlib]System.Security.SecureString::Clear()
    IL_0018:  nop
    IL_0019:  leave.s    IL_002b
  }  // end .try
  finally
  {
    IL_001b:  ldloc.0
    IL_001c:  ldnull
    IL_001d:  ceq
    IL_001f:  stloc.1
    IL_0020:  ldloc.1
    IL_0021:  brtrue.s   IL_002a
    IL_0023:  ldloc.0
    IL_0024:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0029:  nop
    IL_002a:  endfinally
  }  // end handler
  IL_002b:  nop
  IL_002c:  ldstr      "after"
  IL_0031:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0036:  nop
  IL_0037:  ret
} // end of method Program::Function

As mentioned in the other comments, when using the using statement the way you mentioned, it is only when you have one line.

I would like to add that in C# 8.0 you can use "using declarations" this way:

private void Function()
{
    // Some code here

    using SecureString s = new SecureString();

    // more code here
}

In this way, the lifetime of the using statement will extend to the end of the scope in which it is declared. The using locals will then be disposed in the reverse order in which they are declared.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top