「using」
何これ??という方向けの解説記事です。
【C#】usingの使い方|リソースの解放忘れ防止などに有効(IDisposable,、Dispose)
usginでよく見るソースコードはこんな感じだと思います。
1 2 3 4 | using (SampleClass sample = new SampleClass()) { // 何かの処理 } |
記載の通り、usingを使うことでリソースの解放忘れ防止が可能です。
※リソース:例えばファイルとかね。ファイル開いたのに閉じてないとか、はリソース解放忘れ。
「 using」で囲ってあげることで、最後にDisposeメソッドが必ず実行されリソース解放忘れを防止することができる、という感じです。
リソース解放忘れは普通に不具合につながるものなので、しっかりと意識したいポイントの一つです。
※不具合につながる、というかリソース解放忘れ自体が不具合ですけどね。
それでは、どういう事か具体例で説明していきます。
まずはusingを使わずにリソース解放忘れが起きるとどうなるかを確認
本記事では「リソース」として「ファイル」を取り扱いたいと思います。
ファイルのリソース解放忘れすると、どんなことが起きるのか?
例えばこんなソースコード。
1 2 3 4 5 6 7 | private void buttonFileOpen_Click(object sender, EventArgs e) { FileStream fileStream = File.Open(@"C:\temp\test.txt", FileMode.Create); // リソース解放忘れ=ファイル開いたけど閉じていない } |
ボタン押したら、ファイルを新規作成するというコード。
このソースコードだと、ファイルを開きっぱなしで閉じてない。
先ほど新規作成して閉じていないファイル「C:\temp\test.txt」をマウスダブルクリックで開いてみると…。
ファイルにアクセスできない
先ほどファイルオープンした後、クローズ(閉じる)していないので、C#のプログラムがずっとファイル開きっぱなしの状態になってます。
※C#のプログラムから、ファイルを開いても同じような結果になります。
「ファイル開いて閉じてないとまずいことが起こる」というのはなんとなく理解できたと思います。
usingを使わずにリソース解放忘れが起きるとどうなるか|例外発生でリソース解放忘れすることも!
先ほどのソースコードをちょっと改造して、ダメな例をもう一つ。
※何も意味のないソースコードですが気にしないでください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | private void buttonFileOpen_Click(object sender, EventArgs e) { // ファイルを新規作成 FileStream fileStream = File.Open(@"C:\temp\test.txt", FileMode.Create); // 何かしらの処理… /* ------------- */ // わざと例外が発生する処理を実装 int[] array = new int[] { 0, 1 }; // 配列のインデックス指定不正で例外発生させる int arr = array[5]; /* ------------- */ // ファイルを閉じる。 fileStream.Close(); } |
- ファイルをオープン
- 配列インデックス外にアクセスし例外発生
- ファイルを閉じる
こんな流れです。
この時、2の例外が発生すると、3のクローズ処理が実行されません。
※これが分からない人は、実際に実行して試してみてください。
すると、「3」のファイルを閉じる処理が、実行されないことになります。
まずいですね。先ほどの例のように、ずっとファイルを開きっぱなしで、自分の好きなタイミングで再度オープンすることができなくなってしまいます。
そんな時は、よく使うのが try catch だと思います。
まずはusingを使わずに「try catch finally」で考えてみる|解放忘れが起きやすいやつ
「using」を使うのと同等の処理で「try catch finally」があります。
これを使ったソースコード例は以下。
これは良いコード例です
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | private void buttonFileOpen_Click(object sender, EventArgs e) { FileStream fileStream = null; try { // ファイルを新規作成 fileStream = File.Open(@"C:\temp\test.txt", FileMode.Create); // 何かしらの処理… /* ------------- */ // わざと例外が発生する処理を実装 int[] array = new int[] { 0, 1 }; // 配列のインデックス指定不正で例外発生させる int arr = array[5]; /* ------------- */ } catch (Exception ex) { Debug.WriteLine(ex.ToString()); } finally { // finally で必ずファイルを閉じる処理を実行 // ファイルを閉じる。 if (fileStream != null) { fileStream.Close(); } } } |
というように、ファイルオープンやその後の処理をtryでくくりました。
この時、何かしらの処理で例外が発生しても良いように、catchとfinallyも実装しています。
finallyに「ファイルクローズ」を実装することで、必ず最後にはファイルが閉じられるようにしています。
これでリソース解放忘れの心配なし!という感じに仕上がりました。
でも、ちょっと長ったらしくて、ソースコードもうちょっとすっきりさせたいですよね。
また、もっと複雑なソースコードになると、finallyでのファイル閉じる処理が実行されないことも。
※例外をうまくCatch出来てなかったり、考慮出てなかったりで。
そんな時に便利なのが、本記事の主役である「using」です。
usingを使うとリソース解放がスッキリ記述できる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | private void buttonFileOpen_Click(object sender, EventArgs e) { // ファイルを新規作成 using (FileStream fileStream = File.Open(@"C:\temp\test.txt", FileMode.Create)) { // 何かしらの処理… /* ------------- */ // わざと例外が発生する処理を実装 int[] array = new int[] { 0, 1 }; // 配列のインデックス指定不正で例外発生させる int arr = array[5]; /* ------------- */ } } |
先ほどの、try catch finnaly で実装していたファイルオープン→クローズ処理が、こんな簡単にスッキリかけちゃいます。
「FileStream fileStream = File.Open …」をusing でくくるだけでOK。
これで、usingを抜ける時に、自動でファイルを閉じる処理が実行される、という訳です。
実際には、FileStreamのDisposeメソッドが処理されます。その中でCloseされてる感じ。
このようにusginを使うことで、リソース解放忘れをなくすことができたり、ソースコードをスッキリ書く事ができます。
とても便利な奴です。
リソース解放忘れが必要か?確認する方法
というように、リソース解放が必要なものを使う時は、usingが便利そうというのが分かりました。
じゃあ、「がリソース解放が必要なやつなの?」という疑問もでてきます。
そんなときは、「ドット Dispose」で、Disposeメソッドがあるか確認してみましょう。
※以下のように。
ファイルの場合、「fileStream.」と打つと、「Dispose」というメソッドが出てきました。
つまり、FileStreamはDispose(リソース解放)が必要なオブジェクト、という事。
このようにして、リソース解放が必要かのチェックをする癖をつけておくと良いと思います。
※DIsposeメソッドがあるものには,usingが使えますので是非チェックしてみてください。
まとめ|usingでリソース解放忘れを防止しよう。場合によってはコードもスッキリするのでおすすめ!
- リソース解放が必要なものは「using」が使えるかも
- リソース解放忘れには「using」がおすすめ
- try catch finally はコードが複雑でリソース解放忘れの危険もあるよ