8
1

More than 3 years have passed since last update.

.NET経験者ほどハマる!?UiPathのTryCatch(トライキャッチ)挙動について

Last updated at Posted at 2021-08-30

はじめに

過去に Java や VB.Net、C# 等に触れたことがあるのですが、それらに共通する TryCatch 周りの挙動と UiPath の挙動があまりに違っていたことに衝撃を受けたので、そのあたりをまとめてみました。

気づいたきっかけは @masatomix 様の以下 Qiita 記事です。(いつも参考にさせていただいております)

トライキャッチアクティビティが、Finally を実行しないケースがある…ですと…!?
というわけで VB.Net と UiPath でまったく同じソースを作成し、挙動の違い等を検証してみました。

検証環境

  • UiPath
    Studio Pro 2021.6.0-beta.4514(Community License)

  • 使用パッケージ
    UiPath.System.Activities v21.4.1

Finally が実行されないパターン

  1. Exception() 発生
  2. Catch節でログ出力
  3. Catch節で再スロー
  4. Finally節でログ出力

という動作パターンで比較してみます。

VB.Net で実行

VB.Net
Module Program
    Sub Main(args As String())
        Call Sample()
    End Sub

    Sub Sample()
        Try
            Throw New Exception()   ' 例外発生

        Catch ex As Exception
            Console.WriteLine("Catch:Exception")
            Throw    ' 再スロー

        Finally
            Console.WriteLine("Finally")
        End Try
    End Sub
End Module
結果(VB.Net)
Catch:Exception
Unhandled exception. System.Exception: Exception of type 'System.Exception' was thrown.
Finally

:point_up: VB.Net では、Catchブロック内での再スロー後に Finally が実行されていることを確認できました。

UiPath で実行

image.png

結果(UiPath)
[Info] TEST の実行が開始されました。
[Info] Catch:Exception
[Error] スロー: 種類 'System.Exception' の例外がスローされました。
[Info] TEST の実行が終了しました。 in: 00:00:00

まったく同じソースを UiPath で再現したところ、なんと Finally が実行されていません!
VB.Net とは違う動きをしました。
再スローアクティビティは、このあたりを抑えて使用しないと危険ですね。

Finally は、Tryブロック、もしくはCatchブロックを最後まで実行したときのみ、正常に呼ばれる。

再スローアクティビティの対策

では再スローアクティビティは基本使用しないほうがいいのでしょうか?
そうではなく、個人的には上記仕様を理解した上でなら効果的に使えると考えています。

また以下の手法で、再スローアクティビティを使用しつつ Finally も正常に実行できるよう対策可能です。

  • Catch節での再スローを、さらに上位の TryCatch で拾う

先ほど UiPath サンプルで使用したトライキャッチ自体を、さらに外側のトライキャッチで囲んでみました。

image.png

実行結果は以下です。

結果(UiPath)
[Info] TEST の実行が開始されました。
[Info] Catch:Exception
[Debug] 再スロー
[Info] Finally
[Info] 【外側】Finally
[Info] TEST の実行が終了しました。 in: 00:00:00

今度は再スロー後の Finally が実行されました!
どうやら UiPath は以下の仕様のようです。

再スローアクティビティがスローした例外を捕捉する機構が備わっていれば、Finally が正常に実行される。

よって外側をもう一つトライキャッチで囲わなくても、実は グローバル例外ハンドラー を設定するだけで再スロー後に Finally が実行されることも確認できました。

グローバル例外ハンドラー の有無だけで Finally 実行有無が変わる、というのは Studio 上でワークフローをパッと見ただけではなかなか気づけないポイントなので注意が必要です。

Catch節の定義順に伴う到達箇所について

検証していくうちに、Finally 実行有無とは別の違いも見えてきたのでまとめます。

検証:Catch節の定義順によって、VB.Net と UiPath で動作差異がある

VB.Net で実行

VB.Net
Module Program
    Sub Main(args As String())
        Call Sample()
    End Sub

    Sub Sample()
        Try
            Throw New ArgumentNullException()   ' 例外スロー

        Catch ex As Exception
            Console.WriteLine("Catch:Exception")

        Catch ex As SystemException
            Console.WriteLine("Catch SystemException")

        Catch ex As ArgumentException
            Console.WriteLine("Catch:ArgumentException")

        Catch ex As ArgumentNullException
            Console.WriteLine("Catch:ArgumentNullException")
        End Try
    End Sub
End Module

実行前からコンパイラが警告を出している糞コード:sweat_smile:ですが、検証用のためご容赦下さい。
image.png
VB.Net、C#、Java すべてに共通して言えますが、Catch節の定義順というのは意味があります。各例外クラスが親子関係にある場合、親クラスに該当する Catch ブロックが存在するとそこで捕捉されます。

上記例の場合、全ての例外クラスの親クラスである Exception が Catch 節で最初に定義されているため、どんな例外が発生しても Catch ex As Exception で捕捉されてしまい、それ以外の Catch ブロックに到達することはありません。(だからコンパイラが警告を出している)

もちろん実行結果は以下の通りです。

結果(VB.Net)
Catch:Exception

UiPath で実行

image.png

結果(UiPath)
[Info] TEST の実行が開始されました。
[Info] Catch:ArgumentNullException
[Info] TEST の実行が終了しました。 in: 00:00:01

なんと Exception ではなく、ArgumentNullException に到達しました!
ここも VB.Net とは違う動きをするようです。

UiPath のトライキャッチでは、Catch 節の定義順に関係なく、スローされた例外と一致する Catch 節で捕捉される

最後に

トライキャッチ周りは UiPath 独特の動きが多いなー、という印象を持ちました。

次回はUiPathを使ったAPI連携の記事を予定しています。

8
1
0

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
8
1