unchecked (выражение)
unchecked { // операторы, для которых переполнение игнорируется } где выражение обозначает конкретное выражение, при вычислении которого перепол нение игнорируется. Если же в непроверяемом выражении происходит переполнение, то результат его вычисления усекается. Ниже приведен пример программы, в котором демонстрируется применение клю чевых слов checked и unchecked.
// Продемонстрировать применение ключевых слов checked и unchecked. using System;
class CheckedDemo { static void Main() { byte a, b; byte result; a = 127; b = 127; try { result = unchecked((byte)(a b)); Console.WriteLine("Непроверенный на переполнение результат: " + result); result = checked((byte)(a b)); // эта операция приводит к // исключительной ситуации Console.WriteLine("Проверенный на переполнение результат: " + result); //не подлежит выполнению } catch (OverflowException exc) { Console.WriteLine(exc); } } } При выполнении этой программы получается следующий результат.
Непроверенный на переполнение результат: 1 System.OverflowException: Переполнение в результате выполнения арифметической операции. в CheckedDemo.Main() в <имя_файла>:строка 20 Как видите, результат вычисления непроверяемого выражения был усечен. А вы числение проверяемого выражения привело к исключительной ситуации. В представленном выше примере программы было продемонстрировано приме нение ключевых слов checked и unchecked в одном выражении. А в следующем при мере программы показывается, каким образом проверяется и не проверяется на пере полнение целый блок операторов.
// Продемонстрировать применение ключевых слов checked // и unchecked в блоке операторов. using System;
class CheckedBlocks { static void Main() { byte a, b; byte result; a = 127; b = 127; try { unchecked { a = 127; b = 127; result = unchecked((byte)(a * b)); Console.WriteLine("Непроверенный на переполнение результат: " + result); а = 125; b = 5; result = unchecked((byte)(a * b)); Console.WriteLine("Непроверенный на переполнение результат: " + result); } checked { a = 2; b = 7; result = checked((byte)(a * b)); // верно Console.WriteLine("Проверенный на переполнение результат: " + result); а = 127; b = 127; result = checked((byte)(a * b)); // эта операция приводит к // исключительной ситуации Console.WriteLine("Проверенный на переполнение результат: " + result); // не подлежит выполнению } } catch (OverflowException exc) { Console.WriteLine(exc); } }
} Результат выполнения этой программы приведен ниже.
Непроверенный на переполнение результат: 1 Непроверенный на переполнение результат: 113 Проверенный на переполнение результат: 14 System.OverflowException: Переполнение в результате выполнения арифметической операции. в CheckedDemo.Main() в <имя_файма>:строка 41 ``` Как видите, результаты выполнения непроверяемого на переполнение блока опе раторов были усечены. Когда же в проверяемом блоке операторов произошло пере полнение, то возникла исключительная ситуация.
Потребность в применении ключевого слова checked или unchecked может воз никнуть, в частности, потому, что по умолчанию проверяемое или непроверяемое со стояние переполнения определяется путем установки соответствующего параметра компилятора и настройки самой среды выполнения. Поэтому в некоторых програм мах состояние переполнения лучше проверять явным образом.
ГЛАВА 14. Применение средств ввода-вывода
В примерах программ, приводившихся в предыду щих главах, уже применялись отдельные части си стемы ввода-вывода в С#, например метод Console. WriteLine(), но делалось это без каких-либо формальных пояснений. Система ввода-вывода основана в C# на иерар хии классов, поэтому ее функции и особенности нельзя было представлять до тех пор, пока не были рассмотрены классы, наследование и исключения. А теперь настал черед и для ввода-вывода. В C# применяется система ввода-вывода и классы, определенные в среде .NET Framework, и поэтому рассмотрение ввода-вывода в этом языке относится ко всей системе ввода-вывода среды .NET в целом.
В этой главе речь пойдет о средствах консольного и фай лового ввода-вывода. Следует, однако, сразу же предупре дить, что система ввода-вывода в C# довольно обширна. По этому в этой главе рассматриваются лишь самые важные и наиболее часто используемые ее средства. Организация системы ввода-вывода в C# на потоках
Ввод-вывод в программах на C# осуществляется посред ством потоков. Поток — это некая абстракция производ ства или потребления информации. С физическим устрой ством поток связывает система ввода-вывода. Все потоки действуют одинаково — даже если они связаны с разными физическими устройствами. Поэтому классы и методы ввода-вывода могут применяться к самым разным типам устройств. Например, методами вывода на консоль можно пользоваться и для вывода в файл на диске. Байтовые и символьные потоки
На самом низком уровне ввод-вывод в С# осуществляется байтами. И делается это потому, что многие устройства ориентированы на операции ввода-вывода отдельными байтами. Но человеку больше свойственно общаться символами. Напомним, что в C# тип char является 16-разрядным, а тип byte — 8-разрядным. Так, если в целях ввода- вывода используется набор символов в коде ASCII, то для преобразования типа char в тип byte достаточно отбросить старший байт значения типа char. Но это не годится для набора символов в уникоде (Unicode), где символы требуется представлять двумя, а то и больше байтами. Следовательно, байтовые потоки не совсем подходят для ор ганизации ввода-вывода отдельными символами. С целью разрешить это затруднение в среде .NET Framework определено несколько классов, выполняющих превращение байтового потока в символьный с автоматическим преобразованием типа byte в тип char и обратно. Встроенные потоки
Для всех программ, в которых используется пространство имен System, доступны встроенные потоки, открывающиеся с помощью свойств Console.In, Console.Out и Console.Error. В частности, свойство Console.Out связано со стандартным по током вывода. По умолчанию это поток вывода на консоль. Так, если вызывается метод Console.WriteLine(), информация автоматически передается свойству Console. Out. Свойство Console.In связано со стандартным потоком ввода, который по умол чанию осуществляется с клавиатуры. А свойство Console.Error связано со стан дартным потоком сообщений об ошибках, которые по умолчанию также выводятся на консоль. Но эти потоки могут быть переадресованы на любое другое совместимое устройство ввода-вывода. Стандартные потоки являются символьными. Поэтому в эти потоки выводятся и вводятся из них символы. Классы потоков
В среде .NET Framework определены классы как для байтовых, так и для символь ных потоков. Но на самом деле классы символьных потоков служат лишь оболочками для превращения заключенного в них байтового потока в символьный, автоматически выполняя любые требующиеся преобразования типов данных. Следовательно, сим вольные потоки основываются на байтовых, хотя они и разделены логически. Основные классы потоков определены в пространстве имен System.IO. Для того чтобы воспользоваться этими классами, как правило, достаточно ввести приведенный ниже оператор в самом начале программы. using System.IO;
Пространство имен System.IO не указывается для консольного ввода-вывода по тому, что для него определен класс Console в пространстве имен System. Класс Stream
Основным для потоков является класс System.IO.Stream. Он представляет бай товый поток и является базовым для всех остальных классов потоков. Кроме того, он является абстрактным классом, а это означает, что получить экземпляр объекта класса Stream нельзя. В классе Stream определяется ряд операций со стандартными пото ками, представленных соответствующими методами. В табл. 14.1 перечислен ряд наи более часто используемых методов, определенных в классе Stream.
Таблица 14.1. Некоторые методы, определенные в классе Stream Метод Описание void Close() Закрывает поток void Flush() Выводит содержимое потока на физическое устройство int ReadByte() Возвращает целочисленное представление следующего байта, доступного для ввода из потока. При обнаружении конца файла возвращает значение -1 int Read(byte[] buffer, int offset, int count) Делает попытку ввести count байтов в массив buffer, начиная с элемента buffer[offset]. Возвращает количество успешно введенных байтов long Seek(long offset, SeekOrigin origin) Устанавливает текущее положение в потоке по указанному смещению offset относительно заданного начала отсчета origin. Возвращает новое положение в потоке void WriteByte(byte value) Выводит один байт в поток вывода void Write(byte[]buffer, int offset, int count) Выводит подмножество count байтов из массива buffer, начиная с элемента buffer[offset]. Возвращает количество выведенных байтов
Некоторые из методов, перечисленных в табл. 14.1, генерируют исключение IOException при появлении ошибки ввода-вывода. Если же предпринимается по пытка выполнить неверную операцию, например вывести данные в поток, предназна ченный только для чтения, то генерируется исключение NotSupportedException. Кроме того, могут быть сгенерированы и другие исключения — все зависит от кон кретного метода.
Следует заметить, что в классе Stream определены методы для ввода (или чтения) и вывода (или записи) данных. Но не все потоки поддерживают обе эти операции, поскольку поток можно открывать только для чтения или только для записи. Кроме того, не все потоки поддерживают запрос текущего положения в потоке с помощью метода Seek(). Для того чтобы определить возможности потока, придется воспользо ваться одним, а то и несколькими свойствами класса Stream. Эти свойства перечисле ны в табл. 14.2 наряду со свойствами Length и Position, содержащими длину потока и текущее положение в нем.