J Bryan Price
J Bryan Price

Reputation: 1384

Can I clear an external variable inside a using block?

I have a static StreamWriter field for a log file I need to access through a lambda function that listens to StandardOutput on a long-running Process.

I'm using the null/not-null status of the field to determine if the Process is busy on another thread; the actions need to be performed sequentially.

My question is, what happens when I set my variable to null inside a using block? Will it still get disposed properly?

public class Service
{
    private static StreamWriter logger;

    void Run(string logFile)
    {
        using (logger = new StreamWriter(logFile))
        {
            /* ... */

            logger = null;
        }
    }
}

Upvotes: 4

Views: 1325

Answers (3)

doppelgreener
doppelgreener

Reputation: 5124

My question is, what happens when I set my variable to null inside a using block? Will it still get disposed properly?

That depends on where your variable's declared. It will either be disposed of correctly, or your code won't compile in the first place.

If your variable is declared outside the using statement: Yes

A a;
using (a = new A())
{
    a = null;
}

Yes, it will be disposed properly. For a simple test:

A variable is nulled within a using block and disposed of successfully
My text colour scheme is Ragnarok Grey.

Even with the assignment cleared, a reference to the new A() seems to have been maintained. a is disposed of at the end of the using statement as expected, even if it's nulled inside.

In some versions of Visual Studio, this may result in Compiler Warning (level 2) CS0728:

Possibly incorrect assignment to local 'a' which is the argument to a using or lock statement. The Dispose call or unlocking will happen on the original value of the local.

If your variable declared within the using statement: N/A

using (var a = new A())
{
    a = null;
}

The above code won't compile. You're not permitted to assign to a using variable in the first place. The above code produces this compile error:

Cannot assign to 'a' because it is a 'using variable'

This is Compiler Error CS1656.

This error occurs when an assignment to variable occurs in a read-only context. Read-only contexts include foreach iteration variables, using variables, and fixed variables. To resolve this error, avoid assignments to a statement variable in using blocks, foreach statements, and fixed statements.

Upvotes: 2

Hans Passant
Hans Passant

Reputation: 942267

This is not a problem. You'd have to look at the IL with ildasm.exe to see how the compiler does this. But it generates an extra variable to store a reference to the object so you cannot shoot your foot like this. The equivalent C# code would (roughly) look like this:

StreamWriter $temp = new StreamWriter(logFile);
logger = $temp;
try {
   // etc...
   logger = null;
}
finally {
   if ($temp != null) $temp.Dispose();
}

That extra $temp variable keeps you out of trouble.

Upvotes: 2

Henk Holterman
Henk Holterman

Reputation: 273711

According to the C# reference, § 8.13, your code:

private static StreamWriter logger;

using (logger = new StreamWriter(logFile))
{
   /* ... */
   logger = null;
}

is equivalent to

private static StreamWriter logger;


{  // using scope
   logger = new StreamWriter(logFile);
   IDisposable resource = logger;       // hidden var inserted by the compiler 
   try
   {
     /* ... */
     logger = null;
   }
   finally
   {
      if (resource != null)  
        resource.Dispose();
   }
}

The relevant quote:

A using statement of the form

    using (expression) statement

has the same three possible expansions, but in this case ResourceType is implicitly the compile-time type of the expression, and the resource variable is inaccessible in, and invisible to, the embedded statement.

Upvotes: 3

Related Questions