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 を実装する任意の型で使用できます。StreamReader、SqlConnection、HttpClient などの組み込みクラスはすべてこのインターフェイスを実装しているため、適切なリソース管理を簡単かつ確実に行えます。
チャレンジ
簡単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() が自動的に呼び出されます。
たとえば、入力が MainDB と SELECT * FROM Users の場合、出力は次のようになります。
Connection opened: MainDB
Executing on MainDB: SELECT * FROM Users
Connection closed: MainDBusing ブロックが終了すると接続が自動的に閉じられることに注目してください。手動で 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 メソッドが自動的に呼び出されます
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
5ポリモーフィズムとインターフェース
コンパイル時 vs 実行時ポリモーフィズムインターフェース vs 抽象クラス複数のインターフェース明示的なインターフェースアップキャストとダウンキャストまとめ - 図形計算機8オブジェクト指向の高度な概念
継承よりコンポジションジェネリクス (クラスとメソッド)デリゲートとイベント属性とリフレクションIDisposable と using ステートメント依存性の注入 (DI) の基礎自分で練習してみよう: C#オンラインコンパイラ