InnerException の再スローには ExceptionDispatchInfo を使おう

単発タスク(Task.WaitAllとかで集約していないタスク)を使った非同期処理を扱うメソッドでの例外を投げる場合、 AggregateException をそのまま出すと、メソッドを呼ぶ側で InnerException をチェックしたりしないといけないので面倒です(面倒ですよね?)。

/// <exception cref="AggregateException">非同期処理がIOExceptionした場合. InnerException参照のこと</exception>
void hoge() {
  Task t = Task.Run(() => { // ※ 裏で進められる IOException が出るかもしれない処理 });
  // ※ 表の処理
  t.Wait();    // AggregateException 出るかも
}

try {
  hoge();
} catch (AggregateException e) {
  if (e.InnerException is IOException) {
    // IOException の処理
  }
}

InnerException を throw するとスタックトレースが破棄されて(デバッグ時に)使い物にならない。

/// <exception cref="IOException">IOException出た</exception>
void hoge() {
  Task t = Task.Run(() => { // ※ 裏で進める IOException が出るかもしれない処理 });
  // ※ 表の処理
  try {
    t.Wait();    // AggregateException 出るかも
  } catch (AggregateException e) {
    throw e.InnerException;
  }
}

try {
  hoge();
} catch (IOException e) {
  // IOException の処理
  // ※ e のスタックトレースは throw e.InnerException 時点なので…
}

なので、 .Net 4.5 以降に制限されますが、 ExceptionDispatchInfo.Capture を使ってスタックトレースを保ったまま InnerException を throw しましょう。
呼び出し元では IOException 待つだけなので少し幸せに。

/// <exception cref="IOException">IOException出た</exception>
void hoge() {
  Task t = Task.Run(() => { // ※ 裏で進める IOException が出るかもしれない処理 });
  // ※ 表の処理
  try {
    t.Wait();    // AggregateException 出るかも
  } catch (AggregateException e) {
    ExceptionDispatchInfo.Capture(e.InnerException).Throw();  // スタックトレースを保って InnerException を throw
  }
}

try {
  hoge();
} catch (IOException e) {
  // IOException の処理
  // ※ e のスタックトレースは Task 内での例外発生時点 デバッグが楽に
}


await を使えるなら await でよさそうですが、タスクのタイムアウト待ちしたりする場合や async にしたくない場合には使えないので。


.net - In C#, how can I rethrow InnerException without losing stack trace? - Stack Overflow