Menu
Coddy logo textTech

IDisposable と using ステートメント

CoddyのC#ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 45/70。

一部のオブジェクトは、ガベージコレクターが自動的に解放できないリソース(ファイルハンドル、データベース接続、ネットワークソケット)を保持します。IDisposableインターフェイスは、これらのリソースを使い終えたときに解放する標準的な方法を提供します。

クラスは、IDisposableメソッドを提供することでDispose()を実装します:

public class FileHandler : IDisposable
{
    private bool disposed = false;
    
    public void Dispose()
    {
        if (!disposed)
        {
            Console.WriteLine("Releasing resources...");
            disposed = true;
        }
    }
}

Dispose() を手動で呼び出すこともできますが、その行に到達する前に例外が発生したらどうなるでしょうか? using ステートメントを使うと、問題が発生した場合でもクリーンアップが確実に行われます。

using (FileHandler handler = new FileHandler())
{
    // ハンドラーを使って作業する
}  // ここで Dispose() が自動的に呼び出される

現代の C# では、using 宣言によって、スコープ外になるとオブジェクトを破棄する、より簡潔な構文が利用できます。

void ProcessFile()
{
    using FileHandler handler = new FileHandler();
    // handler を使って作業する
}  // メソッド終了時に Dispose() が呼び出される

using ステートメントは、IDisposable を実装する任意の型で使用できます。StreamReaderSqlConnectionHttpClient などの組み込みクラスはすべてこのインターフェイスを実装しているため、適切なリソース管理を簡単かつ確実に行えます。

challenge icon

チャレンジ

簡単

IDisposable インターフェースと using ステートメントを使用して、適切なリソース管理を実演するデータベース接続シミュレーターを作成しましょう。接続がいつ開かれ、閉じられたかを追跡するクラスを作成し、リソースが常に適切に解放されるようにします。

コードを2つのファイルに分けて整理します。

  • DatabaseConnection.cs: DataAccess 名前空間に IDisposable を実装する DatabaseConnection クラスを作成します。接続には次の機能を持たせます。
    • コンストラクターを通じて設定される ConnectionName プロパティ(string、読み取り専用)を持つ
    • private の boolean フィールドを使用して、接続が破棄済みかどうかを追跡する
    • オブジェクトが作成されたとき(コンストラクター内)に Connection opened: {ConnectionName} を出力する
    • Dispose() メソッドを実装して Connection closed: {ConnectionName} を出力する。ただし、まだ破棄されていない場合のみとする(二重破棄を防止)
    • Executing on {ConnectionName}: {sql} を返す Query(string sql) メソッドを持つ
  • Program.cs: メインファイルで、データベース接続に using ステートメントを使用して適切なリソース管理を実演します。接続を作成し、クエリを実行して、using ブロックにクリーンアップを自動的に処理させます。

入力は2つ受け取ります。

  • 接続名(例: MainDB
  • 実行する SQL クエリ(例: SELECT * FROM Users

using ステートメントを使用して接続を作成します。ブロック内で Query メソッドを呼び出し、その結果を出力します。using ブロックが終了すると、Dispose() が自動的に呼び出されます。

たとえば、入力が MainDBSELECT * FROM Users の場合、出力は次のようになります。

Connection opened: MainDB
Executing on MainDB: SELECT * FROM Users
Connection closed: MainDB

using ブロックが終了すると接続が自動的に閉じられることに注目してください。手動で Dispose() を呼び出すことを覚えておく必要はありません。このパターンにより、ブロック内で例外が発生した場合でも、リソースが常にクリーンアップされます。

自分で試してみよう

using System;
using DataAccess;

class Program
{
    public static void Main(string[] args)
    {
        string connectionName = Console.ReadLine();
        string sqlQuery = Console.ReadLine();
        
        // TODO: 'using' ステートメントを使用して DatabaseConnection を作成する
        // using ブロックの中で:
        // 1. sqlQuery を指定して Query メソッドを呼び出す
        // 2. Query メソッドの結果を出力する
        // using ブロックが終了すると、Dispose メソッドが自動的に呼び出されます
        
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: C#オンラインコンパイラ