How C# Using Declarations Work Without Curly Braces

Introduced in C# 8.0, the using declaration allows developers to manage IDisposable objects cleanly without wrapping code in nested curly braces. This article explains how declaration-style using statements work under the hood, how their scope and lifetime are determined, the order in which resources are disposed of, and when you should still consider using the traditional block syntax.

The Traditional using Statement vs. using Declaration

Historically, disposing of a resource required a using statement with an explicit code block:

using (var stream = new FileStream("test.txt", FileMode.Open))
{
    // Work with stream
} // stream.Dispose() is called here

While effective, working with multiple resources led to deeply nested code (often called the "pyramid of doom").

A using declaration removes the parentheses and the curly braces by prepending the using keyword directly to a local variable declaration:

using var stream = new FileStream("test.txt", FileMode.Open);
// Work with stream

How the Lifetime and Scope Work

Instead of defining lifetime through an explicit block marked by { ... }, a using declaration binds the lifetime of the variable to the enclosing scope in which it is declared.

The compiler automatically emits a try/finally block behind the scenes. The resource's Dispose() method is guaranteed to run when the program execution exits the containing scope—whether that exit happens via a return statement, an exception, or reaching the end of the block.

Consider the following method:

void ProcessData()
{
    using var reader = new StreamReader("data.txt");
    string line = reader.ReadLine();
    Console.WriteLine(line);
} // reader.Dispose() is invoked here

The variable reader lives until ProcessData finishes. The closing brace } of the method acts as the boundary where disposal occurs.

Disposal Order with Multiple Declarations

When you declare multiple resources within the same scope using this syntax, they are disposed of in reverse order of their declaration (Last-In, First-Out):

void CopyData()
{
    using var source = File.OpenRead("source.bin");
    using var destination = File.Create("destination.bin");

    source.CopyTo(destination);
} 
// 1. destination.Dispose() is called first
// 2. source.Dispose() is called second

This order prevents dependency issues, ensuring that secondary resources depending on earlier ones are safely closed first.

Controlling Scope Without Methods

Because a using declaration lasts until the end of its enclosing scope, it respects any set of curly braces, such as loops, conditional statements, or arbitrary blocks:

void ExecuteTask()
{
    // Arbitrary block to limit resource lifetime
    {
        using var client = new HttpClient();
        // client is used here
    } // client.Dispose() is called here

    // Perform long-running work where the client is no longer needed
}

When to Avoid using Declarations

While using declarations reduce visual clutter, the traditional block-scoped using statement is still useful in scenarios where a resource locks a file or consumes significant memory and must be released immediately—well before the surrounding method completes. In such cases, the explicit boundary of a traditional using block ensures the resource is not held open unnecessarily.