Обработка исключений для C#

1- What is Exception?

Сначала давайте посмотрим на следующий иллюстративный пример:

В этом примере есть часть кода ошибки, которая получается из деления на 0. Деление на 0 вызывает исключение: DivideByZeroException
HelloException.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExceptionTutorial
{
    class HelloException
    {
        public static void Main(string[] args)
        {

            Console.WriteLine("Three");

            // This division no problem.
            int value = 10 / 2;

            Console.WriteLine("Two");

            // This division no problem.
            value = 10 / 1;

            Console.WriteLine("One");

            int d = 0;

            // This division has problem, divided by 0.
            // An error has occurred here.
            value = 10 / d;

            // And the following code will not be executed.
            Console.WriteLine("Let's go!");

            Console.Read();
        }
    }
}
Результат выполнения примера:
Вы можете увидеть уведомление на экране консоли. Уведомление об ошибке очень четко, включая информацию о строке code.
Давайте посмотрим на поток программы на следующем рисунке:
  • Обычно программа выполняется с этапа (1), (2) - (5).
  • На этапе (6) программа делитс на 0.
  • Программа выскочит из основного метода, и строка кода (7) не будет выполнена.

Мы будем модифицировать код обоих примеров:

HelloCatchException.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExceptionTutorial
{
    class HelloCatchException
    {
        public static void Main(string[] args)
        {

            Console.WriteLine("Three");

            // This division no problem.        
            int value = 10 / 2;


            Console.WriteLine("Two");

            // This division no problem.     
            value = 10 / 1;


            Console.WriteLine("One");


            int d = 0;

            try
            {
                // This division has problem, divided by 0.
                // An error has occurred here.   
                value = 10 / d;

                // And the following code will not be executed.
                Console.WriteLine("Value =" + value);
            }
            catch (DivideByZeroException e)
            {
                // The code in the catch block will be executed
                Console.WriteLine("Error: " + e.Message);

                // The code in the catch block will be executed
                Console.WriteLine("Ignore...");

            }

            // This code is executed
            Console.WriteLine("Let's go!");


            Console.Read();
        }
    }

}
И результаты запуска примера:
Мы объясним поток программы следующими иллюстрационными изображениями:
  • Шаги (1) - (6) являются полностью нормальными.
  • Исключение происходит на этапе (7), проблема делится на ноль.
  • Сразу же после выполнения команды в блоке catch сразу выполняется скачок (8).
  • Шаг (9), (10) выполнен.
  • Выполняется этап (11), (12).

2- Exception Hierarchy

Это модель иерархической карты Exception в CSharp.
  • Самый высокий класс - Exception
  • Два прямых подкласса - это SystemError и ApplicationException.

 
Исключение, встроенное в CSharp, обычно происходит (derived) от SystemException. Между тем, исключение программистов должно наследоваться от ApplicationException или его подклассов.
Некоторые общие исключения, доступные в Csharp:

Exception type

Description

Exception

Base class for all exceptions.

SystemException

Base class for all runtime-generated errors.

IndexOutOfRangeException

Thrown by the runtime only when an array is indexed improperly.

NullReferenceException

Thrown by the runtime only when a null object is referenced.

AccessViolationException 

Thrown by the runtime only when invalid memory is accessed.

InvalidOperationException

Thrown by methods when in an invalid state.

ArgumentException

Base class for all argument exceptions.

ArgumentNullException

Thrown by methods that do not allow an argument to be null.

ArgumentOutOfRangeException

Thrown by methods that verify that arguments are in a given range.

ExternalException

Base class for exceptions that occur or are targeted at environments outside the runtime.

COMException

Exception encapsulating COM HRESULT information.

SEHException

Exception encapsulating Win32 structured exception handling information.

3- Handling exception with try-catch

Мы написали класс который унаследует от ApplicationException.
AgeException.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExceptionTutorial
{
   class AgeException : ApplicationException
   {

       public AgeException(String message)
           : base(message)
       {
       }

   }

   class TooYoungException : AgeException
   {


       public TooYoungException(String message)
           : base(message)
       {

       }

   }


   class TooOldException : AgeException
   {


       public TooOldException(String message)
           : base(message)
       {

       }

   }
}
И класс AgeUtils со статическими методами для изучения возраста.
AgeUtils.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExceptionTutorial
{
    class AgeUtils
    {
        // This method checks the age.
        // If age is less than 18, the method will throw an exception TooYoungException
        // If age greater than 40, the method will throw an exception TooOldException
        public static void checkAge(int age)
        {
            if (age < 18)
            {
                // If age is less than 18, an exception will be thrown
                // This method ends here.
                throw new TooYoungException("Age " + age + " too young");
            }
            else if (age > 40)
            {
                // If age greater than 40, an exception will be thrown.
                // This method ends here.
                throw new TooOldException("Age " + age + " too old");
            }
            // If age is between 18-40.
            // This code will be execute.
            Console.WriteLine("Age " + age + " OK!");
        }
    }

}
TryCatchDemo1.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExceptionTutorial
{
   class TryCatchDemo1
   {
       public static void Main(string[] args)
       {

       
           Console.WriteLine("Start Recruiting ...");
         
           Console.WriteLine("Check your Age");
           int age = 50;

           try
           {

               AgeUtils.checkAge(age);

               Console.WriteLine("You pass!");

           }
           catch (TooYoungException e)
           {
           
               Console.WriteLine("You are too young, not pass!");
               Console.WriteLine(e.Message);

           }
           catch (TooOldException e)
           {
             
               Console.WriteLine("You are too old, not pass!");
               Console.WriteLine(e.Message);

           }

          Console.Read();

       }
   }

}
Запуск примера:
В следующем примере мы будем перехватывать исключения через родительские исключения (класс супер исключений).
TryCatchDemo2.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExceptionTutorial
{
    class TryCatchDemo2
    {
        public static void Main(string[] args)
        {
          
            Console.WriteLine("Start Recruiting ...");
       
            Console.WriteLine("Check your Age");
            int age = 15;

            try
            {
                // Here can throw TooOldException or TooYoungException
             
                AgeUtils.checkAge(age);

                Console.WriteLine("You pass!");

            }
            catch (AgeException e)
            {
                // If an exception occurs, type of AgeException
                // This catch block will be execute

                Console.WriteLine("Your age invalid, you not pass");
                Console.WriteLine(e.Message);

            }

            Console.Read();
        }
    }

}
Запуск примера:

4- try-catch-finally

Мы привыкли к ошибке catch через блок try-catch. try-catch-finally используется для полной обработки исключения.
try {

  // Do something here

} catch (Exception1 e) {

  // Do something here

} catch (Exception2 e) {

  // Do something here

} finally {

  // Finally block is always executed
  // Do something here

}
TryCatchFinallyDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExceptionTutorial
{
   class TryCatchFinallyDemo
   {
       public static void Main(string[] args)
       {

           String text = "001234A2";

           int value = toInteger(text);

           Console.WriteLine("Value= " + value);

           Console.Read();

       }

       public static int toInteger(String text)
       {
           try
           {

               Console.WriteLine("Begin parse text: " + text);

               // An Exception can throw here (FormatException).
               int value = int.Parse(text);

               return value;

           }
           catch (FormatException e)
           {
               // In the case of 'text' is not a number.
               // This catch block will be executed.  
               Console.WriteLine("Number format exception: " + e.Message);

               // Returns 0 if FormatException occurs
               return 0;

           }
           finally
           {

               Console.WriteLine("End parse text: " + text);

           }
           
       }
   }

}
Запуск примера:
Это поток программы. И finally блок всегда выполняется.

5- Exception Wrapping

Нам нужен некоторый класс, участвующий в этом примере
  • Person: Имитируйте набор участников в компанию с информацией: 
    • Имя, возраст, пол.
  • GenderException: гендерное исключение.
  • ValidateException: исключение оценивает кандидат.
  • ValidateUtils: Класс со статическим методом оценивает кандидатов.
    • Действительно, если возраст от 18 до 40 лет
    • И мужчины.
Person.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExceptionTutorial
{
  class Person
  {

      public static readonly string MALE = "male";
      public static readonly string FEMALE = "female";

      private string name;
      private string gender;
      private int age;

      public Person(string name, string gender, int age)
      {
          this.name = name;
          this.gender = gender;
          this.age = age;
      }

      public string GetName()
      {
          return name;
      }


      public string GetGender()
      {
          return gender;
      }

      public int GetAge()
      {
          return age;
      }

  }

}
GenderException.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExceptionTutorial
{
   class GenderException : ApplicationException
   {

       public GenderException(String message)
           : base(message)
       {


       }
   }

}
Класс ValidateException обёртывает другой Exception:
ValidateException.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExceptionTutorial
{
   class ValidateException : ApplicationException
   {

       // Wrap an Exception
       public ValidateException(Exception e) : base("Something invalid", e)
       {
           
       }
   }

}
ValidateUtils.java
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExceptionTutorial
{

  class ValidateUtils
  {
 
      public static void CheckPerson(Person person)
      {
          try
          {

              // Check age.
              // Valid if between 18-40
              // This method can throw TooOldException, TooYoungException.  
              AgeUtils.checkAge(person.GetAge());

          }
          catch (Exception e)
          {

              // If not valid
              // Wrap this exception by ValidateException, and throw.
              throw new ValidateException(e);

          }

          // If that person is Female, then invalid.
          if (person.GetGender() == Person.FEMALE)
          {

              GenderException e = new GenderException("Do not accept women");
              throw new ValidateException(e);

          }
      }
  }

}
WrapperExceptionDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExceptionTutorial
{
 class WrapperExceptionDemo
 {
     public static void Main(string[] args)
     {

         // One participant recruitment.
         Person person = new Person("Marry", Person.FEMALE, 20);

         try
         {

             // Exceptions may occur here.
             ValidateUtils.CheckPerson(person);

         }
         catch (ValidateException wrap)
         {

             // Get the real cause.
             // May be TooYoungException, TooOldException, GenderException
             Exception cause = wrap.GetBaseException();

             if (cause != null)
             {
                 Console.WriteLine("Message: " + wrap.Message);
                 Console.WriteLine("Base Exception Message: " + cause.Message);
             }
             else
             {
                 Console.WriteLine("Message: " + wrap.Message);

             }
         }

         Console.Read();
     }
 }

}
Запуск примера:

6- Some common exceptions

Теперь вы увидите некоторые примеры с распространённым исключением:

6.1- NullReferenceException

Это одно из наиболее распространенных исключений, которое обычно вызывает ошибку в программе. Исключение отбрасывается при вызове метода или доступа к полям нулевого (null) объекта.
NullReferenceExceptionDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExceptionTutorial
{
   class NullReferenceExceptionDemo
   {
       // For example, here is a method that can return null string.
       public static string GetString()
       {
           if (1 == 2)
           {
               return "1==2 !!";
           }
           return null;
       }

       public static void Main(string[] args)
       {

           // This is an object that references not null.
           string text1 = "Hello exception";

           // Get length of string.
           int length = text1.Length;

           Console.WriteLine("Length text1 = " + length);

           // This is an object that references null.
           String text2 = GetString(); // text2 = null.

           // Get length of string.
           // NullReferenceException will occur here.
           length = text2.Length; // ==> Runtime Error!

           Console.WriteLine("Finish!");


           Console.Read();
       }
   }

}
На самом деле, подобно обработке других исключений, вы можете использовать try-catch, чтобы поймать и обработать это исключение. Тем не менее, это механически, как правило, мы должны проверить, чтобы значение объекта не было null до его использования.

Вы можете исправить приведенный выше код, чтобы сделать его похожим на следующий, избегая NullReferenceException:
// This is a null object.
String text2 = GetString();

// Check to make sure 'Text2' are not null.
// Instead of using try-catch.
if (text2 != null)
{
length = text2.Length;
}
 

6.2- IndexOutOfRangeException

Это исключение возникает, когда вы пытаетесь обратиться к элементу, индекс которого недопустим в массиве. Например, массив содержит 10 элементов, но вы получаете доступ к элементу с индексом 20.
IndexOutOfRangeExceptionDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExceptionTutorial
{
    class IndexOutOfRangeExceptionDemo
    {
        public static void Main(string[] args)
        {

            String[] strs = new String[] { "One", "Two", "Three" };

            // Access to the element has index 0.
            String str1 = strs[0];

            Console.WriteLine("String at 0 = " + str1);


            // Access to the element has index 5.
            // IndexOutOfRangeException occur here.
            String str2 = strs[5];

            Console.WriteLine("String at 5 = " + str2);

            Console.Read();

        }
    }
}
Чтобы избежать IndexOutOfRangeException, вы должны проверить массив вместо использования try-catch.
if (strs.length > 5)
{
String str2 = strs[5];
Console.WriteLine("String at 5 = " + str2);
}
else
{
 Console.WriteLine("No elements with index 5");
}