外部リソースは確実に破棄する
ファイルやデータベース、ネットワークなどの外部リソースを使用する .NET Framework のクラスは使い終わったら Dispose()
メソッドを呼び出してリソースを開放する必要があります。
Dispose
を忘れるとファイルがオープンしたままになって他から利用できなくなったり、メモリリークが発生したりする危険があるため確実に Dispose()
する必要があります。
var reader = new StreamReader(@"Sample.txt");
try
{
string text = reader.ReadToEnd();
Console.WriteLine(text);
}
finally
{
reader.Dispose();
}
上記のように finally
ブロックで確実に Dispose()
を呼び出すこともできますが、
using ステートメント
を使うともっと簡潔にスマートにコーディングできます。
(≠ 名前空間をインクルードする using ディレクティブ
)
using (var reader = new StreamReader(@"sample.txt"))
{
string text = reader.ReadToEnd();
Console.WriteLine(text);
}
using
のブロック {}
を抜けた際に自動で reader.Dispose()
が呼ばれます。
名前も using
なので try {...} finally {...}
より意図が明確です。
また、万が一 Dispose()
を忘れてしまったり、消してしまうこともなくなります。
確実に Dispose()
が呼ばれる
finally
と同様に例外がスローされようが途中でリターンされようが {}
を抜ける際に必ず Dispose()
が呼ばれます。
(IL(.NET Framework の中間言語)を見ると try {...} finally {...}
が生成されているようです。)
string GetText(string path)
{
using (var reader = new StreamReader(path))
{
string text = reader.ReadToEnd();
return text;
}
}
リソースオブジェクトを複数同時に宣言する
同じ型の場合、リソースオブジェクトは1つの using
で複数同時に宣言することができます。
区切りには ,
を使用します。
この場合、型推論 var
は使用することはできません。
using (StreamReader // var は不可
reader1 = new StreamReader(@"Sample1.txt"),
reader2 = new StreamReader(@"Sample2.txt"),
reader3 = new StreamReader(@"Sample3.txt"))
{
// 外部リソースを使った処理
}
複数の using
を多段重ねできる
ネストを作らず多段重ねすることができます。
一つ目の using
の末尾には ;
も {
もついていません。
using (var reader = new StreamReader(@"Sample1.txt"))
using (var writer = new StreamWriter(@"sample2.txt"))
{
// 外部路ソースを使った処理
}
注意点
以下のように、リソースオブジェクトをインスタンス化してから using
ステートメントに渡すこともできますがこれは推奨されていません。
外部リソースへのアクセスがなくなっている可能性が高いですが、変数のスコープが残ってしまいます。
リソースオブジェクトの宣言は using
ステートメント内で行うことが推奨されています。
var reader = new StreamReader(@"Sample.txt");
using (reader)
{
// reader を使った処理
}
// reader はスコープ内だが、
// 外部リソースへのアクセスが失われている可能性があり、
// 例外がスローされる場合がある
string line = reader.ReadLine();
また、using
を使用するリソースオブジェクトは IDisposable
インターフェースの Dispose
メソッドを実装している必要があります。
と言っても、 .NET Framework の外部リソースを扱うクラスは IDisposable
が実装されているので特に気にする必要はありません。
自作する場合でも Visual Studio では リファクタリング機能 ctrl + .
で一瞬でスケルトンコードが生成できるのでそれほど面倒ではないはず。
class MyDisposableClass : IDisposable
{
// 以下自動生成されたコード
#region IDisposable Support
private bool disposedValue = false; // 重複する呼び出しを検出するには
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: マネージ状態を破棄します (マネージ オブジェクト)。
}
// TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下のファイナライザーをオーバーライドします。
// TODO: 大きなフィールドを null に設定します。
disposedValue = true;
}
}
// TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードが含まれる場合にのみ、ファイナライザーをオーバーライドします。
// ~MyDisposableClass() {
// // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
// Dispose(false);
// }
// このコードは、破棄可能なパターンを正しく実装できるように追加されました。
public void Dispose()
{
// このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
Dispose(true);
// TODO: 上のファイナライザーがオーバーライドされる場合は、次の行のコメントを解除してください。
// GC.SuppressFinalize(this);
}
#endregion
}
ここまで読んでいただきありがとうございました。
参考
using ステートメント (C# リファレンス)
[C#]Usingを使え
C# Tips -usingを使え、使えったら使え(^^)-
C#のusingステートメントの機能