[C#] How to Reduce Nesting and Simplify Code with using Declarations

The “using declaration” introduced in C# 8.0 is a feature that drastically simplifies the syntax for resource management.

With traditional using statements, the code readability often suffered due to deep nesting caused by { ... } blocks. By utilizing using declarations, you can extend the variable’s scope to the entire block and implement safe Dispose processing without increasing indentation levels.

In this article, we will use a CSV file reading process as an example to explain the differences between the traditional notation and the new using declaration, as well as how it behaves.

目次

Overview of using Declarations

A using declaration is a syntax where you simply add the using keyword when declaring a variable. This ensures that the variable is automatically Disposed when it goes out of scope.

The Traditional Problem (using Statement)

In the traditional writing style, curly braces {} were necessary to define the lifespan of the resource. If there were multiple resources, the nesting tended to get deep.

// Traditional style
using (var reader = new StreamReader("path/to/file.csv"))
{
    // Indentation gets deep here
    while (!reader.EndOfStream)
    {
        // Processing...
    }
} // Disposed here

The New Solution (using Declaration)

With using declarations, you only need to add using to the variable definition line. Curly braces are not required.

// New style
using var reader = new StreamReader("path/to/file.csv");

// Indentation does not get deeper
while (!reader.EndOfStream)
{
    // Processing...
}
// Automatically Disposed when the method (or current block) ends

Practical Code Example: Reading CSV Data

The following code is an implementation example of a method that reads a CSV file at a specified path and outputs its content to the console. It uses the using declaration to manage the file stream concisely.

using System;
using System.IO;

namespace DataProcessor
{
    class Program
    {
        static void Main()
        {
            string filePath = "employees.csv";
            
            // Create a file (for testing purposes)
            File.WriteAllText(filePath, "ID,Name,Department\n101,Yamada,Sales\n102,Sato,Dev");

            try
            {
                ReadAndPrintCsv(filePath);
            }
            catch (IOException ex)
            {
                Console.WriteLine($"File reading error: {ex.Message}");
            }
        }

        /// <summary>
        /// Reads a CSV file and displays the content.
        /// Uses using declaration for resource management.
        /// </summary>
        static void ReadAndPrintCsv(string path)
        {
            if (!File.Exists(path))
            {
                Console.WriteLine("File does not exist.");
                return;
            }

            Console.WriteLine($"--- Started reading {path} ---");

            // [Point] using declaration
            // This reader object is automatically Disposed (released)
            // the moment we exit the ReadAndPrintCsv method.
            using var reader = new StreamReader(path);

            // Skip the header line
            string header = reader.ReadLine();
            Console.WriteLine($"Header: {header}");

            // Read data lines
            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine();
                if (string.IsNullOrWhiteSpace(line)) continue;

                var columns = line.Split(',');
                Console.WriteLine($"ID: {columns[0]}, Name: {columns[1]}, Dept: {columns[2]}");
            }

            Console.WriteLine("--- Reading complete ---");
            
            // reader.Dispose() is implicitly called here (at the end of the method).
        }
    }
}

Execution Result

--- Started reading employees.csv ---
Header: ID,Name,Department
ID: 101, Name: Yamada, Dept: Sales
ID: 102, Name: Sato, Dept: Dev
--- Reading complete ---

Technical Points and Precautions

1. Timing of Resource Release

The lifespan (scope) of a variable defined with a using declaration lasts until the end of the block where that variable was declared. In the example above, it is released at the end of the method. However, if declared inside an if statement or for loop block, it will be released the moment that block is exited.

2. Improving Readability with Multiple Resources

When handling multiple IDisposable objects, such as a database connection (SqlConnection) and a command (SqlCommand), the effect of using declarations is significant.

Traditional:

using (var conn = new SqlConnection(...))
{
    conn.Open();
    using (var cmd = conn.CreateCommand())
    {
        // Deep nesting
    }
}

Using Declaration:

using var conn = new SqlConnection(...);
conn.Open();
using var cmd = conn.CreateCommand();
// Can be written flat

3. When Explicit Scope is Needed

If you do not want to hold the resource until the end of the method and want to release it earlier, use the traditional using statement (block method) or explicitly create a scope with { } and place the using declaration inside it.

{
    using var tempReader = new StreamReader("temp.txt");
    // Processing...
} // Released here
// Subsequent long processing...

Summary

The using declaration has become one of the best practices for resource management in C#. Since it keeps code nesting shallow and improves readability, it is recommended to actively use using declarations whenever the variable’s scope (lifespan) can safely span the entire method.

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次