LoginSignup
47
43

More than 5 years have passed since last update.

外部リソースの解放には using ステートメントを使う

Last updated at Posted at 2018-03-31

外部リソースは確実に破棄する

ファイルやデータベース、ネットワークなどの外部リソースを使用する .NET Framework のクラスは使い終わったら Dispose() メソッドを呼び出してリソースを開放する必要があります。
Disposeを忘れるとファイルがオープンしたままになって他から利用できなくなったり、メモリリークが発生したりする危険があるため確実に Dispose() する必要があります。

finallyで確実にDisposeする
var reader = new StreamReader(@"Sample.txt");
try
{
    string text = reader.ReadToEnd();
    Console.WriteLine(text);
}
finally
{
    reader.Dispose();
}

上記のように finally ブロックで確実に Dispose() を呼び出すこともできますが、
using ステートメント を使うともっと簡潔にスマートにコーディングできます。
(≠ 名前空間をインクルードする 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 {...} が生成されているようです。)

ブロック内でreturnしてもDisposeされる
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
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ステートメントの機能

47
43
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
47
43