Введение в MSIL - Часть 5: Обработка исключений

Дата публикации 25 сен 2006

Введение в MSIL - Часть 5: Обработка исключений — Архив WASM.RU

В этой части "Введения в MSIL" я расскажу о конструкциях, которые CLI предоставляет для обработки исключений.

Блок try используется для защиты блока инструкций. Если одна из них бросает исключение или один из методов явно или неявно, вызываемых в защищённом блоке, то контроль передаётся соответствующему обработчику инструкций. Блок try объявляется с помощью директивы .try.

Код (Text):
  1.  
  2. .try
  3. {
  4.     /* защищённый код */
  5.  
  6.     leave.s _CONTINUE
  7. }
  8. <exception handler>
  9.  
  10. _CONTINUE:

Последняя инструкция в блоке try - это leave.s, которая передаётся контроль код с меткой _CONTINUE. Эта инструкция гарантирует, что стек будет очищен, а соответствующие блоки finally будут выполнены. Из этого следует, что если выход из защищённого блока произошёл каким-либо другим образом, то было брошено исключение. Обработчик исключения должен следовать непосредственно за блоком try.

CLI предлагает четыре разных типа обработчика, из которых вы можете выбирать в зависимости от вашего языка или приложения. Давайте рассмотрим каждый из них.

Catch

Обработчик catch или блок catch является одним из самых известных, так как он напрямую предоставляется как C++, так и C#. Этот блок объявляется с помощью ключевого слова catch, вместе с которым задаётся тип исключения, для которого предназначается данный обработчик, а также сам код, которому передаётся контроль. Catch-блоки также для удобство могут быть объединены в цепь и использовать один блок try. Посмотрите следующий пример:

Код (Text):
  1.  
  2. .try
  3. {
  4.     ldstr "I'm not a number"
  5.     // ldnull
  6.     // ldstr "123"
  7.     call int32 [mscorlib]System.Int32::Parse(string)
  8.  
  9.     leave.s _CONTINUE
  10. }
  11. catch [mscorlib]System.ArgumentNullException
  12. {
  13.     callvirt instance string [mscorlib]System.Exception::get_Message()
  14.     call void [mscorlib]System.Console::WriteLine(string)
  15.  
  16.     leave.s _CONTINUE
  17. }
  18. catch [mscorlib]System.FormatException
  19. {
  20.     callvirt instance string [mscorlib]System.Exception::get_Message()
  21.     call void [mscorlib]System.Console::WriteLine(string)
  22.  
  23.     leave.s _CONTINUE
  24. }

Здесь мы просим метод Int32::Parse обработать "I'm not a number", что очевидным образом вызывает исключение FormatException. Первый catch-обработчик никогда не запускается. Обработчик FormatException послушно пишет в консоль сообщение, описывающее ошибку, а затем вызывает инструкцию leave.s, чтобы передать контроль коду по метке _CONTINUE. Где находился объект исключения? Среда выполнения гарантирует, что ссылка на исключения будет помещена в стек прежде, чем будет вызван обработчик. Вы можеет поэкспериментировать, закомментировав инструкцию ldstr и раскомментировав ldnull. Это поместит в стек null-ссылку, из-за чего вознинет исключение ArgementNullException.

Filter

Для C++-программиста filter-обработчик является довольно странной конструкцией. Данный обработчик предоставляет блок видимости, где он может решить, хочет ли он обрабатывать исключение. Если да, то в стек помещается значение 1, в противном же случае - 0. Далее следует блок обработки, где, собственно, и находится код, обрабатывающий исключение.

Код (Text):
  1.  
  2. .try
  3. {
  4.     // ldstr "I'm not a number"
  5.     ldnull
  6.     // ldstr "123"
  7.     call int32 [mscorlib]System.Int32::Parse(string)
  8.  
  9.     leave.s _CONTINUE
  10. }
  11. filter
  12. {
  13.     ldstr "filter evaluation\n\t"
  14.     call void [mscorlib]System.Console::Write(string)
  15.    
  16.     callvirt instance string [mscorlib]System.Exception::get_Message()
  17.     call void [mscorlib]System.Console::WriteLine(string)
  18.  
  19.     ldc.i4.1
  20.     endfilter
  21. }
  22. {
  23.     ldstr "filter handler\n\t"
  24.     call void [mscorlib]System.Console::Write(string)
  25.    
  26.     callvirt instance string [mscorlib]System.Exception::get_Message()
  27.     call void [mscorlib]System.Console::WriteLine(string)
  28.  
  29.     leave.s _CONTINUE
  30. }

Try-блок вызовет исключение ArgumentNullException, так как в стек в качестве аргумента метода Int32::Parse была помещена пустая ссылка.

Фильтру даётся шанс определить, будет ли он обрабатывать исключение или нет. В данном случае он просто пишет сообщение об ошибке и помещает в стек значение 1, используя инструкцию ldc.i4.1, чтобы указать, что он собирается обрабатывать исключение. Инструкция endfillter вызывается, чтобы возвратиться из фильтра.

Затем вызывается блок обработки. Обратите внимание, что как один, так и другой блок могут обратиться к исключению на лету, взяв соответствующий объект из стека. Держите в уме, что могут быть брошены ссылки на объекты различных типов, поэтому не ожидайте, что ссылка на объект, которую вы достанете из стека в блоке обработки обязательно будет экземпляров System.Exception. Это не проблема для catch-обработчика, так как вы уже знаете тип исключения.

Finally

Finally-обработчик исключения должны узнать C#- и C++-программисты, знакомые с Structured Exception Handling (SEH). Блок finally, ассоцированный с блоком try выполняется всегда, вне зависимости от того, передаёт ли последний контроль нормальным образом или в результате исключения. Это предоставляет надёжный механизм для того, чтобы гарантировать выполнение определённого блока кода для языков, которые не предоставляют деструкторы, такие как C и C#. Хотя язык С не использует CLI, SEH часто применяется, и слово __finally, предоставляемое компилятором Microsoft C/C++, используется именно для этой цели.

Код (Text):
  1.  
  2. .try
  3. {
  4.     /* защищённый код */
  5.    
  6.     leave.s _CONTINUE
  7. }
  8. finally
  9. {
  10.     /* очищающий код */
  11.  
  12.     endfinally
  13. }

Fault

Fault-обработчик исключения похож на finally, не считая того, что он запускается только если в ассоциированном с ним блоке try произошло исключение. После того, как отрабатывает fault-обработчик, исключение продолжает искать соответствующий catch-обработчик.

Код (Text):
  1.  
  2. .try
  3. {
  4.     /* защищённый код */
  5.    
  6.     leave.s _CONTINUE
  7. }
  8. fault
  9. {
  10.     /* очищающий код */
  11.  
  12.     endfault
  13. }

Теперь настало время сменить тему и в следующей части мы поговорим о том, как всё это соотносится с такими популярными языками программирования, как C# и C++/CLI. © Кенни Керр, пер. Aquila


0 1.420
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532