C#

【C#】try-catch-finallyを使ってみよう

More than 3 years have passed since last update.

備忘録というよりはまあ、他人に教える時に「ここに書いてるから!」って言って楽したいので。


とりあえず書いてみる

例として、今回は以下の様な動作を行う。


フォームロード時にC:\temp.txtを読み込み、内容をメッセージボックスで表示する


ごくごく単純。

で、ファイルを読んだらそれの後処理も必ず行うとする。

まっすぐにコードを書くと、こんな感じ。


Form1.cs

private void Form1_Load(object sender, EventArgs e)

{
string filePath = @"C:\temp.txt";
System.IO.StreamReader sr = new StreamReader(filePath, Encoding.Default);
MessageBox.Show(sr.ReadToEnd());
sr.Close();
}

まあ間違ってはないんだけど。

これだと、「読み込みに失敗した時にsrがクローズされない」という問題が発生する。

で、これを回避するためにtry-catch-finallyを使う。


try-catch-finallyを使う

try-catch-finallyを使うと、



  1. tryの処理中に

  2. 予期せぬ事態(例外)が発生したら


  3. tryの処理を中断してcatchの処理を行う


  4. trycatchの処理が全て終わるとfinallyの処理を行う

て感じにできる。

今回の場合だと、読込中に例外が発生したか否かに関わらずsr.Close();を行いたいから、


Form1.cs

private void Form1_Load(object sender, EventArgs e)

{
string filePath = @"C:\temp.txt";
System.IO.StreamReader sr = null;

try
{
sr = new System.IO.StreamReader(filePath, Encoding.Default);
MessageBox.Show(sr.ReadToEnd());
}
catch (Exception)
{
MessageBox.Show("Error!!");
}
finally
{
if (sr != null)
{
sr.Close();
}
}
}


tryの前にsrを宣言してるのは、try内で宣言されたものはcatchfinallyでは参照できないから。

スコープの問題、というやつですね。

あとsr = nullのままfinallyの処理を行う(nullのものをClose()する)のはよろしくないので、

クローズ処理の前に判定を置いてる。

で、これでOK…かと思いきや、そうじゃない。確かに後処理はしてるんだけど、

これだと「メッセージボックスが表示されてる間ファイルがロックされる」という事態に。


クローズ処理は先に行いたい

一度読み込んだらもうファイルに用はないので、すぐにクローズしてしまいたい。

じゃあどうすんの、てことですが。

try-catchの中にtry-finallyを入れるてことをします。

これはまず見たほうが早そう。


Form1.cs


private void Form1_Load(object sender, EventArgs e)
{
string filePath = @"C:\temp.txt";

try
{
System.IO.StreamReader sr = null;
string context = string.Empty;

try
{
sr = new System.IO.StreamReader(filePath, Encoding.Default);
context = sr.ReadToEnd();
}
finally
{
if (sr != null)
{
sr.Close();
}
}

MessageBox.Show(context);
}
catch (Exception)
{
MessageBox.Show("Error!!");
}
}


おわかりいただけるだろうか。

最初のtryの中で例外が発生したら、catchに飛ばされる。

この段階ではsrnullなので、クローズ処理は必要ない。

続いて次のtryに入る。この中で例外が発生したら、まず最初にfinallyに飛ばされる。

ここでクローズ処理。その後catchに飛ばされる。

不思議に思われるかもしれないが、2つめのtry内の処理は結局1つめのtry内に存在しているので、

2つめのtryで発生した例外はすなわち1つめのtryで発生した例外とも言えるのだ。

よってここで先にクローズ処理が行われた後、エラーメッセージが表示される。

無事データを読み終わったらsrのデータを変数に退避させ、クローズ処理。

そしてメッセージボックスを表示。ここで例外が発生しても、

既にsrのクローズ処理は行っているので、ファイルがロックされているなどということはない。

完璧である。


分割しよう

若干処理が長くなるのが欠点ではあるが、確実にクローズ処理を行ってからその他の処理を行うので、

面倒な事態を回避することが可能。

自分の場合、大抵は以下のようにメソッドを分けて使う。


Form1.cs

private void Form1_Load(object sender, EventArgs e)

{
string filePath = @"C:\temp.txt";

try
{
string context = getContext(filePath);
MessageBox.Show(context);
}
catch (Exception)
{
MessageBox.Show("Error!!");
}
}

private string getContext(string filePath)
{
System.IO.StreamReader sr = null;

try
{
sr = new System.IO.StreamReader(filePath, Encoding.Default);

return sr.ReadToEnd();
}
finally
{
if (sr != null)
{
sr.Close();
}
}
}


詳しい処理順序は知らないけど、returnがあってもちゃんとクローズ処理はしてくれるらしい。

優秀。

読み込みメソッドなんかは使い回すことが多々あるので、try-catchだけイベント側に書いて、

try-finallyをメソッド側に書いておくことで、エラーメッセージを使い分けたりもできる。

ドヤ顔で語って来ましたが、まあこれも教えていただいた内容なんですけどね。

確かにこの方法なら、エラーメッセージ表示中にファイルの編集ができない!みたいなことも回避できます。すごい。

テキストに関わらず、ファイルを使った読み書き、またデータベースとのやり取りでも上記の方法でできる。

ぜひお試しあれ。