個人的にあまり詳しくない「例外処理」について調べてみました。 「try{} catch{}」を使うのが例外処理なのだな、というぼやけた認識でいるのですが、具体的にどういった場面で利用するのかなど実践で使えるのか、処理は重くないのかなど考えてみました。
例外処理
「try{} catch{}」を使って簡単な例外処理を書いてみます。 次のコードはDictionaryのkeyが不正だった場合に例外処理を行うようにしました。 [c] void Start() { dict = new Dictionary<string, string>(); dict.Add("hoge", "ほげ"); dict.Add("start", "スタート"); dict.Add("end", "エンド");
// 処理
try
{
Debug.Log(_dict["hogo"]);
}
catch()
{
Debug.LogWarning("例外が発生しました");
}
}
[/c]
tryの中のスコープ内で例外処理が発生した場合、catchのスコープ内の処理が呼び出されます。 通常ですとKeyNotFoundExceptionが発生しますが、これを回避することができます。
なお今回の処理は普通なら通常は次のようなコードを書くため例外処理を必要ありません。 [c] // 普通の処理 string text = string.Empty; if(_dict.TryGetValue("hoge",out text)) { Debug.Log(text); } [/c]
例外ごとにcatchの処理を分ける
例外の種類ごとにcatchの処理を分けることができます。 [c] try { Debug.Log(_dict["hogo"]); } catch(KeyNotFoundException e) { Debug.LogWarning(e); } [/c]
catchは一つだけではなく、複数書くこともできます。
finally
「try{} catch{}」にfinallyをつければ、例外が出る・出ないにかかわらず実行される処理を書くことができます。 [c] finally { Debug.Log("最終的処理"); } [/c]
throw
throwを使うと該当箇所に例外を投げることができます。 [c] string hoge = string.Empty; if (_dict.TryGetValue("end", out hoge)) { Debug.Log(hoge); } else { throw new KeyNotFoundException(); } [/c]
いろいろ調べてのですが、throwをどの場面で使った良いのかはわかりませんでした。 別に例外を投げなくてもログを吐けばよいのではと思います。
例外の使い所
ざっと調べてみて、例外処理に関してはこちらが注意をすれば実装しなくても良い場合が多いと感じました。 よく見かけるエラーログはNullReferenceExceptionで、これはNullチェックを念入りに行えば回避することができますし、例で上げたKeyNotFoundExceptionなども人為的な実装ミスになるので、事前に何かしらの判定を入れて回避が可能だと感じました。
ではどこで使ったらよいかと考えると、実装以外の何かしらの不備が発生する箇所です。 具体的には、AssetBundleを含む外部からデータのダウンロードする際になります。 データそのものがない場合は論外ですが、例えば通信不備で本来あるはずのAssetがない場合などです。 いくら実装を正しく行ったとしても起きる可能性がある現象で、このような場合に例外処理を使うのではないかと思いました。(例外が出たら再度読み込む処理をする、もしくは通信エラーのダイアログを出すなど) 普段はそういった通信周りを関係ないことをしているので「例外処理」はあまり馴染みがないのだと同時に感じました。