try-catch-finally徹底解説

C#

一般的な使い方

try
{
  例外が投げられる可能性のあるコード
}
catch(例外の種類)
{
  例外処理コード
}
finally
{
  例外発生の有無にかかわらず実行したいコード
  リソースの破棄などを行う
}
using System;
using System.IO;
using ExceptionSample;

namespace csharp_learn
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("~~~~~~Entry~~~~~~");
            try
            {
                //実行のために、リソースを取得
                Console.WriteLine("~~~~~~try~~~~~~");
                ExceptionClass e = new ExceptionClass();
                e.throw_divisionZero();
            }
            catch (DivideByZeroException)
            {
                Console.WriteLine("~~~~~~catch DivideByZeroException~~~~~~");
            }
            finally
            {
                //tryで取得したリソースを解放
                Console.WriteLine("~~~~~~finally~~~~~~");
            }
            Console.WriteLine("~~~~~~Finish~~~~~~");
        }
    }
}
using System;

namespace ExceptionSample
{ 
    public class ExceptionClass
    {
        public void throw_divisionZero()
        {
            throw new DivideByZeroException();
        }
    }
}

出力結果:
~~~~~~Entry~~~~~~
~~~~~~try~~~~~~
~~~~~~catch DivideByZeroException~~~~~~
~~~~~~finally~~~~~~
~~~~~~Finish~~~~~~

逆アセンブリ分析

Main関数が三つ分けられている。必ず3回実行される仕組みです。

00007FFC5D660FAD  ?? ?????? 
00007FFC5D660FAE  add         byte ptr [rax],al  
--- E:\099_Technology\C#\csharp_learn\Program.cs -------------------------------
     1: using System;
     2: using System.IO;
     3: using ExceptionSample;
     4: 
     5: namespace csharp_learn
     6: {
     7:     class Program
     8:     {
     9:         static void Main(string[] args)
    10:         {
00007FFC5D660FB0  push        rbp  
00007FFC5D660FB1  push        rdi  
00007FFC5D660FB2  push        rsi  
00007FFC5D660FB3  sub         rsp,50h  
00007FFC5D660FB7  mov         rbp,rsp  
00007FFC5D660FBA  xor         eax,eax  
00007FFC5D660FBC  mov         qword ptr [rbp+40h],rax  
00007FFC5D660FC0  mov         qword ptr [rbp+38h],rax  
00007FFC5D660FC4  mov         qword ptr [rbp+30h],rax  
00007FFC5D660FC8  mov         qword ptr [rbp+48h],rax  
00007FFC5D660FCC  mov         qword ptr [rbp+20h],rsp  
00007FFC5D660FD0  mov         qword ptr [rbp+70h],rcx  
00007FFC5D660FD4  cmp         dword ptr [7FFC5D6FFA78h],0  
00007FFC5D660FDB  je          csharp_learn.Program.Main(System.String[])+032h (07FFC5D660FE2h)  
00007FFC5D660FDD  call        00007FFCBD29D7A0  
00007FFC5D660FE2  nop  
    11:             Console.WriteLine("~~~~~~Entry~~~~~~");
00007FFC5D660FE3  mov         rcx,2309CE030C0h  
00007FFC5D660FED  mov         rcx,qword ptr [rcx]  
00007FFC5D660FF0  call        CLRStub[MethodDescPrestub]@7ffc5d660668 (07FFC5D660668h)  
00007FFC5D660FF5  nop  
    12:             try
    13:             {
00007FFC5D660FF6  nop  
    14:                 //実行のために、リソースを取得
    15:                 Console.WriteLine("~~~~~~try~~~~~~");
00007FFC5D660FF7  mov         rcx,2309CE030C8h  
00007FFC5D661001  mov         rcx,qword ptr [rcx]  
00007FFC5D661004  call        CLRStub[MethodDescPrestub]@7ffc5d660668 (07FFC5D660668h)  
00007FFC5D661009  nop  
    16:                 ExceptionClass e = new ExceptionClass();
00007FFC5D66100A  mov         rcx,7FFC5D7221D8h  
00007FFC5D661014  call        CORINFO_HELP_NEWSFAST (07FFCBD177840h)  
00007FFC5D661019  mov         qword ptr [rbp+38h],rax  
00007FFC5D66101D  mov         rcx,qword ptr [rbp+38h]  
00007FFC5D661021  call        CLRStub[MethodDescPrestub]@7ffc5d660658 (07FFC5D660658h)  
00007FFC5D661026  mov         rcx,qword ptr [rbp+38h]  
00007FFC5D66102A  mov         qword ptr [rbp+40h],rcx  
    17:                 e.throw_divisionZero();
00007FFC5D66102E  mov         rcx,qword ptr [rbp+40h]  
00007FFC5D661032  cmp         dword ptr [rcx],ecx  
00007FFC5D661034  call        CLRStub[MethodDescPrestub]@7ffc5d660650 (07FFC5D660650h)  
00007FFC5D661039  nop  
    18:             }
00007FFC5D66103A  nop  
00007FFC5D66103B  nop  
00007FFC5D66103C  jmp         csharp_learn.Program.Main(System.String[])+08Eh (07FFC5D66103Eh)  
    22:             }
00007FFC5D66103E  nop  
00007FFC5D66103F  jmp         csharp_learn.Program.Main(System.String[])+091h (07FFC5D661041h)  
00007FFC5D661041  mov         rcx,rsp  
00007FFC5D661044  call        csharp_learn.Program.Main(System.String[])+0EFh (07FFC5D66109Fh)  
00007FFC5D661049  nop  
    28:             Console.WriteLine("~~~~~~Finish~~~~~~"); //該当行を実行前に07FFC5D66109Fhのfinallyが先に実行する。
00007FFC5D66104A  mov         rcx,2309CE030E0h  
00007FFC5D661054  mov         rcx,qword ptr [rcx]  
00007FFC5D661057  call        CLRStub[MethodDescPrestub]@7ffc5d660668 (07FFC5D660668h)  
00007FFC5D66105C  nop  
    29:         }
00007FFC5D66105D  nop  
00007FFC5D66105E  lea         rsp,[rbp+50h]  
00007FFC5D661062  pop         rsi  
00007FFC5D661063  pop         rdi  
00007FFC5D661064  pop         rbp  
00007FFC5D661065  ret  
     1: using System;
     2: using System.IO;
     3: using ExceptionSample;
     4: 
     5: namespace csharp_learn
     6: {
     7:     class Program
     8:     {
     9:         static void Main(string[] args)
    10:         {
00007FFC5D661066  push        rbp  
00007FFC5D661067  push        rdi  
00007FFC5D661068  push        rsi  
00007FFC5D661069  sub         rsp,30h  
00007FFC5D66106D  mov         rbp,qword ptr [rcx+20h]  
00007FFC5D661071  mov         qword ptr [rsp+20h],rbp  
    19:             catch (DivideByZeroException)
00007FFC5D661076  mov         qword ptr [rbp+30h],rdx  
    20:             {
00007FFC5D66107A  nop  
    21:                 Console.WriteLine("~~~~~~catch DivideByZeroException~~~~~~");
00007FFC5D66107B  mov         rcx,2309CE030D0h  
00007FFC5D661085  mov         rcx,qword ptr [rcx]  
00007FFC5D661088  call        CLRStub[MethodDescPrestub]@7ffc5d660668 (07FFC5D660668h)  
00007FFC5D66108D  nop  
    22:             }
00007FFC5D66108E  nop  
00007FFC5D66108F  nop  
00007FFC5D661090  lea         rax,[csharp_learn.Program.Main(System.String[])+08Eh (07FFC5D66103Eh)]  
00007FFC5D661097  add         rsp,30h  
00007FFC5D66109B  pop         rsi  
00007FFC5D66109C  pop         rdi  
00007FFC5D66109D  pop         rbp  
00007FFC5D66109E  ret  
     1: using System;
     2: using System.IO;
     3: using ExceptionSample;
     4: 
     5: namespace csharp_learn
     6: {
     7:     class Program
     8:     {
     9:         static void Main(string[] args)
    10:         {
00007FFC5D66109F  push        rbp  
00007FFC5D6610A0  push        rdi  
00007FFC5D6610A1  push        rsi  
00007FFC5D6610A2  sub         rsp,30h  
00007FFC5D6610A6  mov         rbp,qword ptr [rcx+20h]  
00007FFC5D6610AA  mov         qword ptr [rsp+20h],rbp  
    23:             finally
    24:             {
00007FFC5D6610AF  nop  
    25:                 //tryで取得したリソースを解放
    26:                 Console.WriteLine("~~~~~~finally~~~~~~");
00007FFC5D6610B0  mov         rcx,2309CE030D8h  
00007FFC5D6610BA  mov         rcx,qword ptr [rcx]  
00007FFC5D6610BD  call        CLRStub[MethodDescPrestub]@7ffc5d660668 (07FFC5D660668h)  
00007FFC5D6610C2  nop  
    27:             }
00007FFC5D6610C3  nop  
00007FFC5D6610C4  nop  
00007FFC5D6610C5  add         rsp,30h  
00007FFC5D6610C9  pop         rsi  
00007FFC5D6610CA  pop         rdi  
00007FFC5D6610CB  pop         rbp  
00007FFC5D6610CC  ret  
00007FFC5D6610CD  add         byte ptr [rax],al  
00007FFC5D6610CF  add         byte ptr [rcx],bl  
00007FFC5D6610D1  or          al,byte ptr [7FFC64691ADCh]  
00007FFC5D6610D7  xchg        eax,edx  
00007FFC5D6610D8  add         esp,dword ptr [rax+2]  
00007FFC5D6610DB  jo          csharp_learn.Program.Main(System.String[])+012Eh (07FFC5D6610DEh)  
00007FFC5D6610DD  push        rax  
00007FFC5D6610DE  add         byte ptr [rax],al  
00007FFC5D6610E0  add         byte ptr [rax],al  
00007FFC5D6610E3  add         byte ptr [rcx],bl  
00007FFC5D6610E5  ?? ?????? 
    27:             }
00007FFC5D6610E6  add         al,0  
00007FFC5D6610E8  ?? ?????? 
    27:             }
00007FFC5D6610E9  push        rdx  
00007FFC5D6610EA  add         esp,dword ptr [rax+2]  
00007FFC5D6610ED  jo          csharp_learn.Program.Main(System.String[])+0140h (07FFC5D6610F0h)  
00007FFC5D6610EF  push        rax  
00007FFC5D6610F0  add         byte ptr [rax],al  
00007FFC5D6610F3  add         byte ptr [rcx],bl  
00007FFC5D6610F5  ?? ?????? 
    27:             }
00007FFC5D6610F6  add         al,0  
00007FFC5D6610F8  ?? ?????? 
    27:             }
00007FFC5D6610F9  push        rdx  
00007FFC5D6610FA  add         esp,dword ptr [rax+2]  
00007FFC5D6610FD  jo          csharp_learn.Program.Main(System.String[])+0150h (07FFC5D661100h)  
00007FFC5D6610FF  push        rax  
00007FFC5D661100  add         byte ptr [rax],al  
00007FFC5D661103  add         byte ptr [rax],al  
--- ソース ファイルがありません -------------------------------------------------------------

try-return-catch-finally

tryの中にreturnがあったら、catchとfinallyを実行されるでしょうか。finallyを実行されたとして、finally実行後の関数処理は実行されるか。さっそく検証してみましょう。

using System;
using System.IO;
using ExceptionSample;

namespace csharp_learn
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("~~~~~~Entry~~~~~~");
            try
            {
                //実行のために、リソースを取得
                Console.WriteLine("~~~~~~try~~~~~~");
                ExceptionClass e = new ExceptionClass();

                return;
                e.throw_divisionZero();
            }
            catch (DivideByZeroException)
            {
                Console.WriteLine("~~~~~~catch DivideByZeroException~~~~~~");
            }
            finally
            {
                //tryで取得したリソースを解放
                Console.WriteLine("~~~~~~finally~~~~~~");
            }
            Console.WriteLine("~~~~~~Finish~~~~~~");
        }
    }
}

出力結果:
~~~~~~Entry~~~~~~
~~~~~~try~~~~~~
~~~~~~finally~~~~~~

結論をまとめます。
1. tryの中に例外が発生させる前に、returnしているため、例外発生しない。
2. tryの中にreturnしてもfinallyは実行される。
3. tryの中にreturnすると、finally処理後の関数内の処理は実行しない。
4. try-catch-finallyの仕組みは従来の関数の仕組みではない。関数の場合は、return処理すると、関数の処理はが終了してしまう。

try-catch-return-finally

catchの中にreturnすると、finallyの処理が実行されるか。finally実行後の関数処理は実行されるか。

using System;
using System.IO;
using ExceptionSample;

namespace csharp_learn
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("~~~~~~Entry~~~~~~");
            try
            {
                //実行のために、リソースを取得
                Console.WriteLine("~~~~~~try~~~~~~");
                ExceptionClass e = new ExceptionClass();
                e.throw_divisionZero();
            }
            catch (DivideByZeroException)
            {
                Console.WriteLine("~~~~~~catch DivideByZeroException~~~~~~");
                return;
            }
            finally
            {
                //tryで取得したリソースを解放
                Console.WriteLine("~~~~~~finally~~~~~~");
            }
            Console.WriteLine("~~~~~~Finish~~~~~~");
        }
    }
}

出力結果:
~~~~~~Entry~~~~~~
~~~~~~try~~~~~~
~~~~~~catch DivideByZeroException~~~~~~
~~~~~~finally~~~~~~

結論:
1. catchでreturnがあっても、finallyの処理も実行される。
2. catchでreturnがあれば、finally実行後の関数処理は実行されない。

try-catch-finally-return

finallyの中にreturn句はビルドエラーです。
error CS0157: コントロールが finally 句の本体から出られません

tryのみ使えるか

using System;
using System.IO;
using ExceptionSample;

namespace csharp_learn
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("~~~~~~Entry~~~~~~");

            try
            {
                //実行のために、リソースを取得
                Console.WriteLine("~~~~~~try~~~~~~");
                //ExceptionClass e = new ExceptionClass();
                //e.throw_divisionZero();
            }
            Console.WriteLine("~~~~~~Finish~~~~~~");
        }
    }
}

ビルドエラー
error CS1524: catch または finally が必要です

catchのみ使えるか

using System;
using System.IO;
using ExceptionSample;

namespace csharp_learn
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("~~~~~~Entry~~~~~~");
            catch (DivideByZeroException)
            {
                Console.WriteLine("~~~~~~catch DivideByZeroException~~~~~~");
                return;
            }
            Console.WriteLine("~~~~~~Finish~~~~~~");
        }
    }
}

ビルドエラー
error CS1003: 構文エラーです。’try’ が必要です

finallyのみ使えるか

using System;
using System.IO;
using ExceptionSample;

namespace csharp_learn
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("~~~~~~Entry~~~~~~");
            finally
            {
                //tryで取得したリソースを解放
                Console.WriteLine("~~~~~~finally~~~~~~");
            }
            Console.WriteLine("~~~~~~Finish~~~~~~");
        }
    }
}

ビルドエラー
error CS1003: 構文エラーです。’try’ が必要です

try-catchのみ使えるか

using System;
using System.IO;
using ExceptionSample;

namespace csharp_learn
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("~~~~~~Entry~~~~~~");
            try
            {
                //実行のために、リソースを取得
                Console.WriteLine("~~~~~~try~~~~~~");
                ExceptionClass e = new ExceptionClass();
                e.throw_divisionZero();
            }
            catch (DivideByZeroException)
            {
                Console.WriteLine("~~~~~~catch DivideByZeroException~~~~~~");
            }
            Console.WriteLine("~~~~~~Finish~~~~~~");
        }
    }
}

出力結果:
~~~~~~Entry~~~~~~
~~~~~~try~~~~~~
~~~~~~catch DivideByZeroException~~~~~~
~~~~~~Finish~~~~~~

try-finallyのみ使えるか

using System;
using System.IO;
using ExceptionSample;

namespace csharp_learn
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("~~~~~~Entry~~~~~~");
            try
            {
                //実行のために、リソースを取得
                Console.WriteLine("~~~~~~try~~~~~~");

            }

            finally
            {
                //tryで取得したリソースを解放
                Console.WriteLine("~~~~~~finally~~~~~~");
            }
            Console.WriteLine("~~~~~~Finish~~~~~~");
        }
    }
}

出力結果:
~~~~~~Entry~~~~~~
~~~~~~try~~~~~~
~~~~~~finally~~~~~~
~~~~~~Finish~~~~~~

catch-finallyのみ使えるか

using System;
using System.IO;
using ExceptionSample;

namespace csharp_learn
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("~~~~~~Entry~~~~~~");
            catch (DivideByZeroException)
            {
                Console.WriteLine("~~~~~~catch DivideByZeroException~~~~~~");
            }
            finally
            {
                //tryで取得したリソースを解放
                Console.WriteLine("~~~~~~finally~~~~~~");
            }
            Console.WriteLine("~~~~~~Finish~~~~~~");
        }
    }
}

ビルドエラーです。構文エラーです。’try’ が必要です

try-catch-catch-catch…finally

一つのtryに対して、複数のcatchが可能ですが、catchの優先順位を考慮する必要がある。
優先順位低い例外を一番上に置くべきです。Exceptionを一番上に置いてしまうと、個別の例外処理の実行ができなくなる。

using System;
using System.IO;
using ExceptionSample;

namespace csharp_learn
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("~~~~~~Entry~~~~~~");
            try
            {
                //実行のために、リソースを取得
                Console.WriteLine("~~~~~~try~~~~~~");
                ExceptionClass e = new ExceptionClass();
                e.throw_divisionZero();
            }
            catch (IOException)
            {
                //try実行の中に異常が発生した場合の処理
                Console.WriteLine("~~~~~~catch IOException~~~~~~");
            }
            catch (DivideByZeroException)
            {
                Console.WriteLine("~~~~~~catch DivideByZeroException~~~~~~");
            }
            catch (Exception)
            {
                Console.WriteLine("~~~~~~catch Exception~~~~~~");
            }
            finally
            {
                //tryで取得したリソースを解放
                Console.WriteLine("~~~~~~finally~~~~~~");
            }
            Console.WriteLine("~~~~~~Finish~~~~~~");
        }
    }
}

出力結果:

~~~~~~Entry~~~~~~
~~~~~~try~~~~~~
~~~~~~catch DivideByZeroException~~~~~~
~~~~~~finally~~~~~~
~~~~~~Finish~~~~~~

次は、例外のcatchの置き場所を検証しましょう。
スーパー型 (‘Exception’) の例外を一番上に置くと、ビルドエラーになる。

using System;
using System.IO;
using ExceptionSample;

namespace csharp_learn
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("~~~~~~Entry~~~~~~");
            try
            {
                //実行のために、リソースを取得
                Console.WriteLine("~~~~~~try~~~~~~");

                ExceptionClass e = new ExceptionClass();
                e.throw_divisionZero();
            }
            catch (Exception)
            {
                Console.WriteLine("~~~~~~catch Exception~~~~~~");
            }
            catch (IOException)
            {
                //try実行の中に異常が発生した場合の処理
                Console.WriteLine("~~~~~~catch IOException~~~~~~");
            }
            catch (DivideByZeroException)
            {
                Console.WriteLine("~~~~~~catch DivideByZeroException~~~~~~");
            }
            finally
            {
                //tryで取得したリソースを解放
                Console.WriteLine("~~~~~~finally~~~~~~");

            }
            Console.WriteLine("~~~~~~Finish~~~~~~");
        }

    }
}

1>—— ビルド開始: プロジェクト: csharp_learn, 構成: Debug Any CPU ——
1>\Program.cs(24,20,24,31): error CS0160: 前の catch 句はこれ、またはスーパー型 (‘Exception’) の例外のすべてを既にキャッチしました
1>\Program.cs(29,20,29,41): error CS0160: 前の catch 句はこれ、またはスーパー型 (‘Exception’) の例外のすべてを既にキャッチしました
1>プロジェクト “csharp_learn.csproj” のビルドが終了しました — 失敗。
========== ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ ==========

try-catch-catch-finally-finally使えるか

using System;
using System.IO;
using ExceptionSample;

namespace csharp_learn
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("~~~~~~Entry~~~~~~");
            try
            {
                //実行のために、リソースを取得
                Console.WriteLine("~~~~~~try~~~~~~");
                ExceptionClass e = new ExceptionClass();
                e.throw_divisionZero();
            }
            catch (DivideByZeroException)
            {
                Console.WriteLine("~~~~~~catch DivideByZeroException~~~~~~");
            }
            catch (Exception)
            {
                Console.WriteLine("~~~~~~catch Exception~~~~~~");
            }
            finally
            {
                //tryで取得したリソースを解放
                Console.WriteLine("~~~~~~finally~~~~~~");
            }
            finally
            {
                //tryで取得したリソースを解放
                Console.WriteLine("~~~~~~finally~~~~~~");
            }
            Console.WriteLine("~~~~~~Finish~~~~~~");
        }
    }
}

ビルドエラー error CS1003: 構文エラーです。’try’ が必要です

マイクロソフト公式のtry-catch-finally (C# リファレンス)

例外処理ステートメント - throw、try、catch、finally
C# throw ステートメントを使用して、例外の発生を通知します。 C# try ステートメントを使用して、コード ブロックで発生した例外をキャッチして処理します。

通常、catch および finally は、try ブロックのリソースを取得して使用する場合に、対で記述されます。catch ブロックで例外的な状況を処理し、finally ブロックでリソースを解放します。

例外の再スローの使用例を含む詳細については、「try-catch」および例外のスローに関するページをご覧ください。 finally ブロックの詳細については、「try-finally」を参照してください。

参考サイト

https://qiita.com/ts7i/items/d7f6c1cd5a14e55943d4
https://kanda-it-school-kensyu.com/java-basic-contents/jb_ch08/jb_0803/
https://docs.microsoft.com/ja-jp/dotnet/csharp/fundamentals/exceptions/

コメント

タイトルとURLをコピーしました