Файл: 1_C#_Введение_в_программирование_Уч_пособ_(Марченко-2005).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 11.12.2025

Просмотров: 1384

Скачиваний: 3

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

можно ли писать в поток – bool CanWrite (если можно – значение должно быть установлено в true),

можно ли задать в потоке текущую позицию – bool CanSeek (если

последовательность, в которой производится чтение-запись не является жёстко детерминированной и возможно позиционирование в потоке - значение должно быть установлено в true),

позицию текущего элемента потока – long Position (возможность позиционирования в потоке предполагает возможность программного изменения значения этого свойства),

общее количество символов потока (длину потока) – long Length.

Всоответствии с общими принципами реализации операций ввода-вывода, для потока предусмотрен набор методов, позволяющих реализовать:

чтение байта из потока с возвращением целочисленного представления СЛЕДУЮЩЕГО ДОСТУПНОГО байта в потоке ввода – int ReadByte(),

чтение определённого (count) количества байтов из потока и размещение их в массиве buff, начиная с элемента buff[index], с возвращением количества успешно прочитанных байтов – int Read(byte[] buff, int index, int count),

запись в поток одного байта – void WriteByte(byte b),

запись в поток определённого (count) количества байтов из массива buff, начиная с элемента buff[index], с возвращением количества успешно записанных байтов – int Write(byte[] buff, int index, int count),

позиционирование в потоке – long Seek (long index, SeekOrigin origin) (позиция текущего байта в потоке задаётся значением смещения index относительно позиции origin),

для буферизованных потоков принципиальна операция флэширования (записи содержимого буфера потока на физическое устройство) – void Flush(),

закрытие потока – void Close().

Множество классов потоков ввода-вывода в Framework.NET основывается (наследует свойства и интерфейсы) на абстрактном классе Stream. При этом классы конкретных потоков обеспечивают собственную реализацию интерфейсов этого абстрактного класса.

Наследниками класса Stream являются, в частности, три класса байтовых потоков:

BufferedStream – обеспечивает буферизацию байтового потока. Как правило, буферизованные потоки являются более производительными по сравнению с небуферизованными,

FileStream – байтовый поток, обеспечивающий файловые операции ввода-вывода,

MemoryStream – байтовый поток, использующий в качестве источника и хранилища информации оперативную память.

Манипуляции с потоками предполагают НАПРАВЛЕННОСТЬ производимых действий.

Информацию из потока можно ПРОЧИТАТЬ, а можно её в поток ЗАПИСАТЬ. Как чтение, так и запись, предполагают реализацию определённых механизмов байтового обмена с устройствами ввода-вывода.

Свойства и методы, объявляемые в соответствующих классах, определяют специфику потоков, используемых для чтения и записи:

TextReader,

TextWriter.

Эти классы являются абстрактными. Это означает, что они не “привязаны” ни к какому конкретному потоку. Они лишь определяют интерфейс (набор методов), который позволяет организовать чтение и запись информации для любого потока.

В частности, в этих классах определены следующие методы, определяющие базовые механизмы символьного ввода-вывода. Для класса TextReader:

int Peek() - Reads the next character without changing the state of the reader or the character source. Returns the next available character without actually reading it from the input stream.

int Read(…) - Overloaded. Несколько одноименных функций с одним и тем же

именем. Читает значения из входного потока. Вариант int Read() предполагает чтение из потока одного символа с возвращением его целочисленного эквивалента или -1 при достижении конца потока. Вариант int Read (char[] buff, int index, int count) и его полный аналог int ReadBlock(char[] buff,

123


int index, int count) обеспечивает прочтение a maximum of count characters

из текущего потока и записывает данные в buffer, начиная at index.

string ReadLine() – Читает строку символов из текущего потока. Возвращается ссылка на объект типа string.

 

string

ReadToEnd()

Читает

все

символы,

начиная

с

текущей

 

позициисимвольного потока, определяемого объектом класса TextReader и

 

возвращает результат как ссылка на объект типа string.

 

 

 

 

void Close() – Закрывает поток ввода.

 

 

 

 

 

Для класса TextWriter, в свою очередь, определяется:

множество перегруженных вариантов функции void Write(…) со значениями параметров, позволяющих записывать символьное представление значений базовых типов (смотреть список базовых типов) и массивов значений (в том числе и массивов строк).

void Flush() - Clears all buffers for the current writer and causes any buffered data to be written to the underlying stream. Очистка буфера вывода с предварительным выводом в поток вывода (носитель данных) содержимого буфера.

void Close() – Закрывает поток вывода.

Эти классы являются базовыми для классов:

StreamReader – содержит свойства и методы, обеспечивающие считывание СИМВОЛОВ из байтового потока,

StreamWriter – содержит свойства и методы, обеспечивающие запись СИМВОЛОВ в байтовый поток.

Вышеуказанные классы включают методы своих “предков” и позволяют реализовать процессы чтения-записи непосредственно из конкретных байтовых потоков. Работа по организации ввода-вывода с использованием этих классов предполагает определение соответствующих объектов, “ответственных” за реализацию операций ввода-вывода с явным указанием потоков, которые предполагается при этом использовать.

Ещё одна пара классов потоков обеспечивает механизмы символьного вводавывода для строк:

StringReader,

StringWriter.

Вэтом случае источником и хранилищем множества символов является символьная строка.

Интересно заметить, что у всех ранее перечисленных классов имеются методы,

обеспечивающие закрытие потоков и не определены методы обеспечивающие открытие соответствующего потока. Потоки открываются в момент создания объекта-представителя соответствующего класса. Наличие функции, обеспечивающей явное закрытие потока принципиально. Оно связано с особенностями выполнения управляемых модулей в Framework.NET. Время начала работы сборщика мусора заранее неизвестно.

Предопределённые потоки ввода-вывода

Ещё одна категория потоков. Предопределённые потоки ввода-вывода используются для реализации ввода-вывода в рамках консольных приложений.

Вклассах предопределённых потоков в отличие от ранее рассмотренных потоков явным образом задаются источник (откуда) и место размещения (куда) информации.

Основные свойства и методы этих потоков определены в классе System.Console. Класс Console является достаточно сложной конструкцией.

Врамках этого класса определены данные-члены, обеспечивающие реализацию предопределённых потоков. В классе System.Console таких потоков три (In, Out, Error).

Ниже описываются свойства этих потоков:

Standard input stream - член класса Console In (в общепринятой нотации

Console.In). Является объектом-представителем класса TextReader. По умолчанию поток In ориентирован на получение информации с клавиатуры,

124


Standard output stream - поток вывода Console.Out. Является объектом-

представителем класса TextWriter. По умолчанию обеспечивает вывод информации на дисплей.

Standard error stream – поток вывода сообщений об ошибках Console.Error.

Также является объектом-представителем класса TextWriter. И также по умолчанию выводит информацию на дисплей.

Вклассе System.Console также определены следующие функции-члены (вернее, множества ПЕРЕГРУЖЕННЫХ функций), обеспечивающих процедуры ввода-вывода:

Write и WriteLine,

Read и ReadLine.

При этом функции-члены класса Console ПО УМОЛЧАНИЮ обеспечивают связь с конкретным потоком. Например, при вызове метода

Console.WriteLine(…); // Список параметров опущен

информация направляется в поток Console.Out. Класс Console также обеспечивает вызов соответствующей функции-члена от имени объектапредставителя класса TextWriter. Непосредственное обращение к члену класса Console (то есть, к потоку), отвечающего за текстовый вывод, имеет следующую форму вызова:

Console.Out.WriteLine(…);

Вызов определённого в классе Console метода ReadLine

Console.ReadLine(…);

обеспечивает получение информации через поток Console.In. Непосредственный вызов аналогичной функции от имени члена класса Console, представляющего класс TextReader, выглядит следующим образом:

Console.In.ReadLine(…);

Функции-представителя класса Console, отвечающей за непосредственное направление информации в поток вывода сообщений об ошибках, не предусмотрено.

Вывод информации об ошибках может быть реализован путём непосредственного обращения к потоку:

Console.Error.WriteLine(…);

Если вспомнить, что объект Error является представителем того же самого класса TextWriter и, так же как и объект Out связан с окном консольного приложения, разделение сообщений, связанных с нормальным ходом выполнения приложения и сообщений об ошибках становится более чем странным.

Информация, выводимая приложением в “штатном” режиме и сообщения об ошибках одинаково направляются в окно приложения на экран консоли. Однако в классе Console реализована возможность перенаправления соответствующих потоков. Например, стандартный поток вывода можно перенаправить в файл, а поток сообщений об ошибках оставить без изменений направленным в окно приложения. Результат очевиден.

Процедуры перенаправления потоков осуществляются с помощью методов void SetIn(TextReader in), void SetOut(TextWriter out), void SetErr(TextWriter err)

и будут рассмотрены ниже.

Функция ToString()

C# является языком, строго и в полном объёме реализующим принципы ООП. В этом языке всё построено на классах и нет ничего, что бы ни соответствовало принципам объектно-ориентированного программирования. Любой элементарный тип является наследником общего класса Object, реализующего, в частности, метод

String ToString(), формирующий в виде human-readable текстовой строки описание тех или иных характеристик типа и значений объектов-представителей

125


данного типа. Любой тип-наследник типа Object (а это ВСЕ типы!) имеет унаследованную, либо собственную переопределённую версию метода ToString(). Применительно к объектам предопределённого типа из CTS, соответствующие версии методов ToString() обеспечивают преобразование значения данного типа к строковому виду.

Всё сделано для программиста. Реализация метода преобразования значения в строку, в конечном счёте, оказывается скрытой от программиста. Именно поэтому вывод значений предопределённых типов с использованием функций Write и WriteLine в программах на языке C# осуществляется так легко и просто. В справочниках по поводу этих функций так и сказано:

The text representation of value is produced by calling Type.ToString.

Эта функция имеет перегруженный вариант, использующий параметр типа string для указания желаемого формата представления. Множество значений этого

параметра ограничено

предопределённым

списком

символов форматирования

(представлены ниже), возможно, в сочетании с целочисленными значениями.

 

 

 

 

Символ форматирования

Описание

 

 

C

Отображение значения

как валюты

с использованием принятого

 

по соглашению символа

 

 

D

Отображение значения как decimal integer

E

Отображение значения в соответствии с научной нотацией

F

Отображение значения как fixed Point

G

Display the number as a fixed-Point or integer, depending on

 

which is the most compact

 

N

Применение запятой для разделения порядков

X

Отображение значения в шестнадцатеричной нотации

Непосредственно за символом форматирования может быть расположена целочисленная ограничительная константа, которая в зависимости от типа выводимого значения может определять количество выводимых знаков после точки, либо общее количество выводимых символов. При этом дробная часть действительных значений округляется, либо дополняется нулями справа. При выводе целочисленных значений ограничительная константа игнорируется, если количество преобразуемых символов превышает её значение. В противном случае преобразуемое значение слева дополняется нулями.

Форматирование используется для преобразования значений “обычных” .NET Framework типов данных в строковое представление да ещё в соответствии с каким-либо общепринятым форматом. Например, целочисленное значение 100 можно представить в общепринятом формате currency для отображения валюты. Для этого можно использовать метод ToString() с использованием символа (стоки?) форматирования (“C”). В результате может быть получена строка вида “$100.00”. И это при условии, что установки операционной системы, компьютера, на котором производится выполнение данного программного кода, соответствуют U.S. English specified as the current culture.

int MyInt = 100;

String MyString = MyInt.ToString(“C”); Console.WriteLine(MyString);

Консольный ввод-вывод. Функции-члены класса Console

При обсуждении процедур ввода-вывода следует иметь в виду одно важное обстоятельство. Независимо от типа выводимого значения, в конечном результате выводится, СИМВОЛЬНОЕ ПРЕДСТАВЛЕНИЕ значения. Это становится очевидным при выводе информации в окно представления, что и обеспечивают по умолчанию методы Console.Write и Console.WriteLine.

WriteLine отличается тем, что завершает свою работу обязательным выводом

Escape-последовательности line feed/carriage return.

Console.WriteLine(“The Captain is on the board!”); // Вывод строки. Console.WriteLine(314); // Вывод символьного представления целочисленного значения. Console.WriteLine(3.14); // Вывод символьного представления значения типа float.

126


Выводимая символьная строка может содержать Escape-последовательности, которые при выводе информации в окно представления позволяют создавать различные “специальные эффекты”.

Методы с одним параметром достаточно просты. Практически всё происходит само собой. Уже на стадии компиляции при выяснении типа выводимого значения подбирается соответствующий вариант перегруженной функции вывода. становится При выводе значения определяется его тип, производится соответствующее стандартное преобразование к символьному виду, которое в виде последовательности символов и выводится в окно представления.

Для облегчения процесса программирования ввода-вывода в C# также используются варианты функций Write и WriteLine с несколькими параметрами.

Эти методы называются ПЕРЕГРУЖАЕМЫМИ (смотреть перегрузку методов). Для программиста C# это означает возможность вызова этих функций с различными параметрами. Можно предположить, что различным вариантам списков параметров функции могут соответствовать различные варианты функций вывода.

Ниже представлен вариант метода WriteLine с тремя параметрами. Во всех случаях, когда количество параметров превышает 1, первый параметр обязан быть символьной строкой.

Console.WriteLine(“qwerty”,314,3.14);

Чтобы понять, как выполняется такой оператор, следует иметь в виду, что всякий параметр метода является выражением определённого типа, которое в процессе выполнения метода может вычисляться для определения значения соответствующего выражения. Таким образом, при выполнении метода WriteLine должны быть определены значения его параметров, после чего в окне приложения должна появиться определённая последовательность символов. Ниже представлен результат выполнения выражения вызова функции:

qwerty

Значения второго и третьего параметров не выводятся.

Дело в том, что первый строковый параметр выражений вызова функций Write и WriteLine используется как управляющий шаблон для представления выводимой информации. Значения следующих за строковым параметром выражений будут выводиться в окно представления лишь в том случае, если первый параметрстрока будет явно указывать места расположения выводимых значений, соответствующих этим параметрам. Явное указание обеспечивается маркерами выводимых значений, которые в самом простом случае представляют собой заключённые в фигурные скобки целочисленные литералы (например, {3}).

При этом способ указания места состоит в следующем:

CLR индексирует все параметры метода WriteLine, следующие за первым параметром-строкой. При этом второй по порядку параметр получает индекс 0, следующий за ним – 1, и т.д. до конца списка параметров.

В произвольных местах параметра-шаблона размещаются маркеры выводимых значений.

Значение маркера должно соответствовать индексу параметра, значение которого должно выводиться на экран.

Значение целочисленного литерала маркера не должно превышать максимального значения индекса параметра.

Таким образом, оператор

Console.WriteLine(“The sum of {0} and {1} is {2}”,314,3.14,314+3.14);

обеспечивает вывод следующей строки:

The sum of 314 and 3.14 is 317.3

В последнем операнде выражения вызова WriteLine при вычислении значения выражения используется неявное приведение типа. При вычислении значения суммы операнд типа int без потери значения приводится к типу float. Безопасные преобразования типов проводятся автоматически.

Несмотря на явную абсурдность выводимых утверждений, операторы

127