Добавить в цитаты Настройки чтения

Страница 264 из 371



Методы класса Convert поддерживают общий способ выполнения преобразований между типами. Класс Convert с одержит 15 статических методов вида Tо <Tуре> (ToBoolean (), ToUInt64 ()), где туре может принимать значения от Boolean до Uint64 для всех встроенных типов, перечисленных в таблице

3.1. Единственным исключением является тип object, — метода ToObject нет по понятным причинам, поскольку для всех типов существует неявное преобразование к типу object. Среди других методов отмечу общий статический метод ChangeType, позволяющий преобразование объекта к некоторому заданному типу.

Существует возможность преобразования к системному типу DateTime, который хотя и не является встроенным типом языка С#, но допустим в программах, как и любой другой системный тип. Приведу простейший пример работы с этим типом:

// System type: DateTime

System.DateTime dat = Convert.ToDateTime("15.03.2003");

Console.WriteLine("Date = {0}", dat);

Результатом вывода будет строка:

Date = 15.03.2003 0:00:00

Все методы Tо <Tуре> класса Convert перегружены и каждый из них имеет, как правило, более десятка реализаций с аргументами разного типа. Так что фактически эти методы задают все возможные преобразования между всеми встроенными типами языка С#.

Кроме методов, задающих преобразования типов, в классе Convert имеются и другие методы, например, задающие преобразования символов Unicode в однобайтную кодировку ASCII, преобразования значений объектов и другие методы. Подробности можно посмотреть в справочной системе.

Проверяемые преобразования

Уже упоминалось о том, что при выполнении явных преобразований могут возникать нежелательные явления, например, потеря точности. Я говорил, что вся ответственность за это ложится на программиста, и легче ему от этого не становится. А какую часть этого бремени может взять на себя язык программирования? Что можно предусмотреть для обнаружения ситуаций, когда такие явления все-таки возникают? В языке C# имеются необходимые для этого средства.

Язык C# позволяет создать проверяемый блок, в котором будет осуществляться проверка результата вычисления арифметических выражений. Если результат вычисления значения источника выходит за диапазон возможных значений целевой переменной, то возникнет исключение (говорят также: "будет выброшено исключение") соответствующего типа. Если предусмотрена обработка исключения, то дальнейшее зависит от обработчика исключения. В лучшем случае, программа сможет продолжить корректное выполнение. В худшем, — она остановится и выдаст информацию об ошибке. Заметьте, не произойдет самого опасного — продолжения работы программы с неверными данными.

Синтаксически проверяемый блок предваряется ключевым словом checked, в теле такого блока арифметические преобразования проверяются на допустимость. Естественно, подобная проверка требует дополнительных временных затрат. Если группа операторов в теле такого блока нам кажется безопасной, то их можно выделить в непроверяемый блок, используя ключевое слово unchecked.

Замечу еще, что и в непроверяемом блоке при работе методов Convert все опасные преобразования проверяются и приводят к выбрасыванию исключений. Приведу пример, демонстрирующий все описанные ситуации:

/// <summary>

/// Демонстрация проверяемых и непроверяемых преобразований.

/// Опасные проверяемые преобразования приводят к исключениям.

/// Опасные непроверяемые преобразования приводят к неверным

/// результатам, что совсем плохо.

/// </summary>

public void CheckUncheckTest()

     x = —25 ^2;

     WhoIsWho ("x", x);

     b= 2 55;

     WhoIsWho("b",b);

     // Проверяемые опасные преобразования.

     // Возникают исключения, перехватываемые catch-блоком,

     checked

     {

         try

         {

             b += 1;

         }

         catch (Exception e)

         {

              Console.WriteLine("Переполнение при вычислении b");

              Console.WriteLine(e);

         }

         try

         {

               b = (byte)x;

          }

          catch (Exception e)

          {

               Console.WriteLine("Переполнение при преобразовании к byte");

               Console.WriteLine(e);





          }

         // непроверяемые опасные преобразования

         unchecked

          {

               try

               {

                   b +=1;

                          WhoIsWho ("b", b);

                   b = (byte)x;

                   WhoIsWho ("b", b);

                   ux= (uint)x;

                   WhoIsWho ("ux", ux);

                   Console.WriteLine("Исключений нет, но результаты не верны!");

               }

               catch (Exception е)

               {

                   Console.WriteLine("Этот текст не должен появляться");

                   Console.WriteLine (е);

                }

               // автоматическая проверка преобразований в Convert

               // исключения возникают, несмотря на unchecked

               try

               }

                    b = Convert.ToByte(х);

               }

               catch (Exception е)

               {

                     Console.WriteLine("Переполнение при преобразовании к byte!");

                     Console.WriteLine(е);

                }

                try

                {

                     ux= Convert.ToUInt32(х);

                }

                catch (Exception e)

                {

                     Console.WriteLine("Потеря знака при преобразовании к uint!");

                      Console.WriteLine (e);

                 }

            }

      }

}

Исключения и охраняемые блоки. Первое знакомство

В этом примере мы впервые встречаемся с охраняемыми try-блоками. Исключениям и способам их обработки посвящена отдельная лекция, но не стоит откладывать надолго знакомство со столь важным механизмом. Как показывает практика программирования, любая вызываемая программа не гарантирует, что в процессе ее работы не возникнут какие-либо неполадки, в результате которых она не сможет выполнить свою часть контракта. Исключения являются нормальным способом уведомления об ошибках в работе программы. Возникновение ошибки в работе программы должно приводить к выбрасыванию исключения соответствующего типа, следствием чего является прерывание нормального хода выполнения программы и передача управления обработчику исключения — стандартному или предусмотренному самой программой.

Заметьте, рекомендуемый стиль программирования в C# отличается от стиля, принятого в языках С/C++, где функция, в которой возникла ошибка, завершается нормальным образом, уведомляя об ошибке в возвращаемом значении результата. Вызывающая программа должна анализировать результат, чтобы понять, была ли ошибка в работе вызванной функции и какова ее природа. При программировании в стиле C# ответственность за обнаружение ошибок лежит на вызванной программе. Она должна не только обнаружить ошибку, но и явно сообщить о ней, выбрасывая исключение соответствующего типа. Вызываемая программа должна попытаться исправить последствия ошибки в обработчике исключения. Подробности смотри в лекции про исключения.

В состав библиотеки FCL входит класс Exception, свойства и методы которого позволяют работать с исключениями как с объектами, получать нужную информацию, дополнять объект собственной информацией. У класса Exception — большое число потомков, каждый из которых описывает определенный тип исключения. При проектировании собственных классов можно параллельно проектировать и классы, задающие собственный тип исключений, который может выбрасываться в случае ошибок при работе методов класса. Создаваемый класс исключений должен быть потомком класса Exception.