Добавим в Решение новый проект, аналогично тому, как был добавлен консольный проект. В качестве типа проекта выберем ПриложениеWindowsForms (WindowsFormsApplication), дадим проекту имя WindowsFormsToMathTools. В результате возникает окноForm1.cs[Конструктор] с объектом Form1, на котором можно будет размещать различные визуальные объекты для организации интерфейса проекта. Чтобы увидеть код проекта, надо, нажав правую кнопкумыши, выбрать командуПерейти к коду. Результатэтой работы показан на рис. 4.1.2-7:
При создании проекта DLL автоматически создавался в проекте один пустой класс, в консольном проекте создавался класс, содержащий метод Main с пустым кодом метода. В Windows проекте автоматически создаются два класса – класс с именем Form1 и класс с именем Program, которые видны в окне Обозреватель решений.
Первый из этих классов является наследником класса Form из библиотеки FCL и наследует все свойства и поведение (методы и события) родительского класса. Класс Form поддерживает организацию интерфейса пользователя в визуальном стиле. Форма является контейнером для размещения визуальных элементов управления – кнопок (Button), текстовых полей (TextBox), списков (ListBox) и более экзотичных элементов – таблиц (DataGridView), деревьев (TreeView) и многих других элементов. С некоторыми элементами управления мы познакомимся уже в этом примере, другие будут встречаться в соответствующих местах нашего курса. Изменим имяклассаForm1 на имя FormResearchSinusточно так же, как при создании проекта DLL, выделив в окне кода имя классаForm1и выбрав в меню Оптимизация кода пунктПереименовать. Затем производим переименование имени файлаForm1.cs на имя FormResearchSinus.cs, которое делается непосредственно в окне проектов Обозреватель решений(SolutionExplorer).
После добавления соответствующего программного кода в классFormResearchSinusон примет вид как на рис. 4.1.2-7, а соответствующая ему форма показана на рис. 4.1.2-7.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using MathTools; namespace WindowsFormsToMathTools { publicpartialclassFormResearchSinus : Form { public FormResearchSinus() { InitializeComponent(); textBox1.Focus(); } privatevoid buttonEvaluate_Click(object sender, EventArgs e) { Double x = Convert.ToDouble(textBox1.Text); textBoxSinStandard.Text = Math.Sin(x).ToString(); textBoxMySin.Text = MathTools.MyMath.Sin(x).ToString(); } } } |
Рис. 4.1.2-7
Класс Program, автоматически создаваемый в Windows проекте, содержит точку входа – статический метод Main(), о важной роли которого мы уже говорили. В отличие от консольного проекта, где тело процедуры Main изначально было пустым и должно было заполняться разработчиком проекта, в Windows проектах процедура Main уже готова и, как правило, разработчиком не изменяется. Чтобы увидеть код процедуры Main(), необходимо, выделив в окне Обозреватель решений файл Program.cs, и нажав правую кнопку мыши, выбрать пункт контекстного менюПерейти к коду. Что же делает автоматически созданная процедура Main(), текст которой можно видеть на рис. 4.1.2-8? Она работает с классом Application библиотеки FCL, вызывая поочередно три статических метода этого класса -EnableVisualStyles, SetCompatibleTextRenderingDefault, Run. О назначении первых двух методов можно судить по их содержательным именам. Основную работу выполняет метод Run - в процессе его вызова создается объект класса FormResearchSinus и открывается форма – визуальный образ объекта, с которой может работать конечный пользователь проекта. Если, как положено, форма спроектирована и заполнена элементами управления, то конечному пользователю остается вводить собственные данные в поля формы, нажимать на кнопки, вообще быть инициатором возникновения различных событий в мире объектов формы. В ответ на возникающие события начинают работать обработчики событий, что приводит к желаемым (или нежеланным) изменениям мира объектов. Типичной ситуацией является проведение вычислений по данным, введенным пользователем и отображение результатов этих вычислений в полях формы, предназначенных для этих целей.
Программный код класса Program приведен на рис. 4.1.2-8.
using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using MathTools; namespace WindowsFormsToMathTools { staticclassProgram { staticvoid Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(newFormResearchSinus()); } } } |
Рис. 4.1.2-8
Построение интерфейса формы
Теперь прокомментируем процесс построения интерфейса – размещение в форме элементов управления. Классическим примером интерфейса, поддерживающего сервисы стандартного класса Math, является инженерный калькулятор. В нашем классе реализована пока только одна функция – sin(x), так что можем построить пока калькулятор одной функции. Но и цели у нас другие.
На рис. 4.1.3-9 показан интерфейс спроектированной формы, который строится в окне FormResearchSinus.cs[Конструктор] (Рис. 4.1.2-9).
Рис. 4.1.2-9
Здесь для полного соответствия следует поменять свойство Textформы со значения Form1 на значениеFormResearchSinus, что делается непосредственно в окне свойств.В форму включено текстовое поле textBox1для ввода значения аргумента x, ещё два текстовых поля textBox2иtextBox3 предназначены для отображения результата вычислений функции sin(x)двумя различными методами. Все текстовые поля снабжены метками, проясняющими смысл каждого поля. В форме находится командная кнопкаbutton1, щелчок по которой приводит к возникновению события Click этого объекта, а обработчик этого события запускает вычисление значений функции и вывод результатов в соответствующие текстовые поля. Каков сценарий работы пользователя? Когда при запуске проекта открывается форма, то пользователь может в соответствующем поле textBox1 задать значение аргумента функции, после чего нажать кнопку с надписью «Вычислить sin(x)». В выходных текстовых полях появятся результаты вычислений. Меняя входные данные можно наблюдать, как меняются результаты вычислений. Можно будет убедиться, что при всех задаваемых значениях аргумента функции значения функции, вычисленное двумя разными методами совпадают с точностью до 9 знаков после запятой.
Для того, чтобы наш Windows - проект корректноработал,в негонеобходимо добавить ссылку на проект с Библиотекой классов(DLL)MathTools, таким же образом, как это было описано консольн0го проекта.
Чтобы получить результаты работы Windowsпроекта, его надо назначить запускаемым проектом. Для этого в окне Обозреватель решенийподведем указатель мыши к имени консольного проектаWindowsFormsToMathTools и из контекстного меню, появляющегося при щелчке правой кнопки, выберем пункт менюНазначить запускаемым проектом.
Окно Обозревателя решений показано на рис. 4.1.2-10.
Рис. 4.1.2-10. Решениеизтрехпроектов – MathTools,
ConsoleMathTools,WindowsFormsToMathTools
4.1.3. Главное меню VS.NET
При работе с C#винтегрированной среде разработки VSможно пользоваться как кнопками стандартной панели инструментов, так и элементами главного меню, расположенными в верхней части экрана (4.1.3-1).
Рис. 4.1.3-1.Стандартная панель инструментов
Главноеменюможетсодержатьследующие элементы:Файл ( File), Правка ( Edit),Вид ( View), Проект ( Project), Построение ( Build), Отладка ( Debug), Данные ( Data), Сервис ( Tools), Окно ( Window)иСправка ( Help).
Первоначально при запуске программы в меню присутствуют лишь некоторые из указанных элементов. Остальные элементы добавляются в меню при открытии дополнительных окон. Например, при открытии проекта в меню добавляются пункты Проект (Project), Построение (Build), Отладка (Debug). Настроить отображение элементов главного меню можно с помощью диалогового окнаНастроить( Customize), котороеоткрывается командой Настроить ( Customize)элемента главного меню Сервис( Tools).
Элемент главного менюФайл ( File )содержит команды, связанные с доступом к файлам.
Команды элемента главного меню Правка (Edit)используются при создании форм и редактировании программного кода.
С помощью команды IntelliSense реализуются возможности технологии IntelliSense, аименно: можно просматривать список членов определенного класса, структуры, объединения или пространства имен и вставлять в программный код подходящий, получить информацию о числе, типах и именах параметров методов и свойств, дописать слова, являющиеся именами методов,
Элемент главного меню Вид ( View )содержит команды вызова окон среды VS. С помощью этих команд могут открываться окна редактора программного кода, конструктора формы, свойств объектов, обозревателя решений и другие окна.
Элемент главного меню Проект (Project)содержит команды, позволяющие добавлять в проект и удалять из неготакие элементы, как форма, программный модуль, класс, а также команды, дающие возможность добавлять ссылки на подключаемые библиотеки.
Последней командой элемента меню Проект ( Proje c t )является команда Свойства: ... (… Properties...), позволяющая открыть окно свойств проекта.
Элемент главного меню Построение (Build)содержит команды, помогающие скомпоновать решение или проект.
Элемент главного меню Отладка ( Debug )содержит команды, предназначенные для отладки и запуска приложения. С помощью команд этого меню можно запустить приложение на выполнение, установить точки останова программы, осуществить пошаговое выполнение приложения, открыть специальные окна для отладки.
Элемент главного меню Формат ( Format )доступен при работе в конструкторе форм.
Он содержит команды, управляющие выравниванием текста и объектов, заданием размеров объектов и определением интервалов между ними. Однако, при работе с различными конструкторами становятся доступными и дополнительные команды. Внизу этого меню располагается команда Блокировка элементов управления ( LockControls),которая позволяет сделать недоступным перемещение элементов управления и сохранить их размер. С ее помощью фиксируются все элементы управления, включая саму форму.
Элемент главного меню Сервис ( Tools )содержит средства для настройки среды разработки, создания макросов, а также команды запуска дополнительных утилит.
Элемент главного меню Окно ( Window ) содержит команды, которые управляют открытыми на экране окнами. С помощью этих команд можно упорядочивать, скрывать окна и переходить из одного окна в другое. Кроме того, команды данного меню позволяют активизировать любое открытое окно.
Элемент главного меню Справка ( Help )использует свои команды для вызова справочной системы с различными вариантами представления информации.
4.1.4. Стандартная панель инструментов и
окна панелей VS
ВVSсодержится большое количество панелей инструментов для отладки и запуска программ, задания расположения элементов на форме и многого другого. Познакомимся со стандартной панелью инструментов (рис. 4.1.4-1), которая используется во всех режимах работы.
Рис. 4.1.4-1. Стандартная панель инструментов
По умолчанию в главном окне программы VisualC# всегда присутствует стандартная панель инструментов, если только вы не удалили ее с экрана. Если для работы необходима стандартная панель инструментов, выберите команду Стандартная ( Standard)элемента меню Панели инструментов ( Toolbars) из элемента главного меню Вид (View).
Окнопанели Начальная страница ( StartPage ) позволяет просмотреть последние использовавшиеся проекты, осуществить поиск примеров программ, как из справки, так и Интернета, а также просмотреть ссылки на сайты, содержащие новости о продукте VS, документацию, учебные пособия.
Начальная страница автоматически открывается при запуске VS. Если же окно Начальная страница( Start Page)не появилось, его можно вызвать с помощью команды Начальная страница ( Start Page)элемента главного меню Вид( View).
Окно панели Конструктор ( Designer )является основным рабочим окном, в котором выполняется визуальное проектирование приложения. Вызвать это окноможно из главного меню командой Конструктор( Designer)элемента главного меню Вид ( View)или двойным щелчком на названии формы в окне Обозреватель решений ( Solution Explorer).
В окне конструктора форм визуально создаются все формы приложения с использованием инструментария среды разработки. Для точного позиционирования объектов на форме в окне можно использовать сетку.
Размер формы в окне можно изменять, используя маркеры выделения формы и мышь. Для изменения размера формы необходимо установить указатель мыши на маркер и, когда он примет вид двунаправленной стрелки, перемещать до требуемого размера.
Для работы в окне Конструктор можно использовать контекстное меню.
Окно панели Редактор кода (CodeEditor)–это мощный текстовый редактор с большим количеством возможностей, являющийся основным инструментом программиста для создания и отладки приложения.
В окне панели Редактор кода (CodeEditor) расположены элементы:
· раскрывающийся список Имя класса (ClassName) содержит перечень объектов приложения; при выборе объекта в этом списке содержимое списка Имя метода (MethodName) изменяется;
· раскрывающийся список Имя Метода (MethodName) дает возможность выбора членов объекта (событий) и автоматического вывода в окно редактора процедуры или шаблона для выбранного члена.
Для работы в окне редактора также можно использовать контекстное меню.
Окно панели Обозреватель решений ( SolutionExplorer )позволяет получить доступ к компонентам, входящим в проект. Если после создания или открытия проекта этого окна нет на экране, то можно его отобразить одним из следующих способов:
· вэлементе главного меню Вид ( View) выбрать команду Обозреватель решений ( SolutionExplorer);
· нажать кнопку Обозреватель решений ( SolutionExplorer) стандартной панелиинструментов;
· нажать комбинацию клавиш < Ctrl>+< Alt>+< L>.
Окно панели Обозреватель решений ( SolutionExplorer) содержит список всех компонентов решения (проекта). В верхней части окна размещается имя решения, а ниже располагаются входящие в него проекты и файлы. Также окно панели Обозреватель решений ( SolutionExplorer) содержит кнопки, отображение которых зависит от типа, выделенного в окне компонента.
Для работы в окне Обозреватель решений ( SolutionExplorer) можно использовать контекстное меню.
Окно панели Панель элементов (Toolbox) - основной рабочий инструмент при визуальной разработке форм приложения. Панель элементов управления вызывается из главного меню Вид (View) командой Панель элементов (Toolbox) или нажатием кнопки Панель элементов (Toolbox) на стандартной панели инструментов.
Окно панели Свойства ( Properties )предназначено для отображения и настройки свойств объектов проекта, включая форму и размещенные в ней объекты. В этом окне, например, содержатся такие свойства выбранного объекта, как позиция в форме, высота, ширина, цвет.
Для открытия диалогового окна Свойства (Properties) следует выполнить одно из следующих действий:
· в элементе главного меню Вид ( View) выбрать команду Окносвойств ( PropertiesWindow);
· нажать кнопку Окносвойств ( PropertiesWindow), расположенную настандартной панели инструментов;
· выбрать команду Свойства ( Properties) контекстного меню выделенного объекта;
· нажать клавишу <F4>.
Поскольку форма и элементы управления каждый сам по себе являются объектами, то набор свойств в этом окне меняется в зависимости от выбранного объекта. С помощью кнопокВ алфавитном порядке( Alphabetical)и По категориям( Categorized)свойства объекта можно просмотреть в алфавитном порядке или по группам (категориям) соответственно.
В нижней части окна появляется подсказка, поясняющая назначение выбранного свойства объекта. Более подробное пояснение можно посмотреть в справочной системе. Также можно воспользоваться динамической справкой, выбрав нужный раздел в окне Динамическая справка( DynamicHelp).
Используя диалоговое окно Свойства ( Properties), можно изменить установленные по умолчанию свойства объектов. Часть свойств объекта, например, размеры и расположение, можно задать перемещением объекта и изменением его размеров с помощью мыши в конструкторе форм. Свойства, установленные в окне свойств, можно изменять при выполнении приложения, написав соответствующие коды в процедурах, создаваемых с помощью редактора кода.
Как правило, форма содержит много объектов. Если выбрать сразу несколько объектов, то в окне свойств можно увидеть общие для этих объектов свойства.
Окно панелиОбозреватель объектов ( ObjectBrowser )предназначенодля просмотра всехобъектов, входящих в состав проекта. Вэтом окне можно получить доступ не только ко всем входящим в проектобъектам, но и их свойствам, методам, событиям. Окно просмотра объектов обычноне визуализировано, и его можно вызвать командой Обозреватель объектов ( ObjectBrowser)из меню Вид ( View).
С помощью раскрывающегося списка Обзор( Browse)окна Обозреватель объектов ( ObjectBrowser) задается область просмотра.
Этот список может содержать следующие значения:
· Все компоненты (AllComponents) -в окнеОбозреватель
объектов (Object Browser) будутотображаться все имеющиеся компоненты;
· Компоненты платформы . NETFramework (. NETFramework)- окно просмотраобъектов будет содержать все библиотеки классов платформы . NETFramework;
· Мое решение ( MySolution) –в окне Обозревательобъектов( ObjectBrowser)будут отображаться содержащиеся в открытом решениипространства имен, классы, структуры, интерфейсы, типы и их свойства, методы,события, переменные, константы;
· Настраиваемый набор компонент ( CustomComponentSet)- в окне
просмотра объектов будет отображаться содержимое компонентов, указанных вдиалоговом окнеИзменить настраиваемый объект компонентов ( EditCustomComponentSet), которое открывается с помощью расположеннойсправа от списка кнопки Добавить (Add). В
качестве компонентов могут выступать проекты решения, СОМ-объекты,внешние библиотеки и исполняемые файлы.
Для задания типа отображаемых компонентов предназначена кнопка Обозреватель объектов: параметры( ObjectBrowser Settings), при нажатии на которую открывается меню со списком типов компонентов: пространства имен, контейнеры, базовые, производные и скрытые типы, открытые, защищенные и скрытые члены классов. При выборе типа слева от его наименования появляется галочка.
Для работы в окне Обозреватель объектов ( ObjectBrowser) можно использовать контекстное меню.
Окно панелиЛокальные ( Locals ) предназначено для просмотра списка локальных переменных приложения и контроля их значений. Вызвать его можно подкомандой Локальные (Locals) команды Окна ( Windows) элемента основного меню Отладка (Debug)или из панели элементов Отладка (Debug).
В окне Локальные (Locals)удобно просматривать имена локальных переменных, объявленных в текущей процедуре, тип и значения этих переменных. Указанная информация появляется в окне автоматически при его вызове. Данное окно используется для отладки и проверки работы приложения. Окно панелиВидимые ( Watch ) предназначенодля более полногоконтроля работы приложения. Это окно вызывается подкомандой Видимые ( Watch) команды Окна ( Windows) элемента основного меню Отладка (Debug)или из панели элементов Отладка (Debug).
Главное меню VS.NET
При работе с C#винтегрированной среде разработки VSможно пользоваться как кнопками стандартной панели инструментов, так и элементами главного меню, расположенными в верхней части экрана (4.1.3-1).
Рис. 4.1.3-1. Стандартная панель инструментов
Главноеменюможетсодержатьследующиеэлементы: Файл ( File), Правка ( Edit), Вид ( View), Проект ( Project), Построение ( Build), Отладка ( Debug), Данные ( Data), Сервис ( Tools), Окно ( Window) иСправка ( Help).
Первоначально при запуске программы в меню присутствуют лишь некоторые из указанных элементов. Остальные элементы добавляются в меню при открытии дополнительных окон. Например, при открытии проекта в меню добавляются пункты Проект (Project), Построение (Build), Отладка (Debug). Настроить отображение элементов главного меню можно с помощью диалогового окнаНастроить( Customize), котороеоткрываемого командой Настроить ( Customize)элемента главного меню Tools(Сервис).
Элемент главного менюФайл ( File )содержит команды, связанные с доступом к файлам.
Команды элемента главного меню Правка (Edit) используются при создании форм и редактировании программного кода.
С помощью команды Технология IntelliSense реализуются возможности технологии
IntelliSense, аименно: можно просматривать список членов определенного класса, структуры, объединения или пространства имен и вставлять в программный код подходящий, получить информацию о числе, типах и именах параметров методов и свойств, дописать слова, являющиеся именами методов,
Элемент главного меню Вид ( View )содержит команды вызова окон среды VS. С помощью этих команд могут открываться окна редактора программного кода, конструктора формы, свойств объектов, обозревателя решений и другие окна.
Элемент главного меню Проект (Project)содержит команды, позволяющие добавлять в проект и удалять из него такие элементы, как форма, программный модуль, класс, а также команды, дающие возможность добавлять ссылки на подключаемые библиотеки.
Последней командой элемента меню Проект( Projec t)является команда Свойства: ... (… Properties...), позволяющая открыть окно свойств проекта.
Элемент главного меню Построение (Build)содержит команды, помогающие скомпоновать решение или проект.
Элемент главного меню Отладка ( Debug )содержит команды, предназначенные для отладки и запуска приложения. С помощью команд этого меню можно запустить приложение на выполнение, установить точки останова программы, осуществить пошаговое выполнение приложения, открыть специальные окна для отладки.
Элемент главного меню Формат ( Format )доступен при работе в конструкторе форм.
Он содержит команды, управляющие выравниванием текста и объектов, заданием размеров объектов и определением интервалов между ними. Однако, при работе с различными конструкторами становятся доступными и дополнительные команды. В низу этого меню располагается команда Блокировка элементов управления ( LockControls),которая позволяет сделать недоступным перемещение элементов управления и сохранить их размер. С ее помощью фиксируются все элементы управления, включая саму форму.
Элемент главного меню Сервис ( Tools )содержит средства для настройки среды разработки, создания макросов, а также команды запуска дополнительных утилит.
Элемент главного меню Окно ( Window ) содержит команды, которые управляют открытыми на экране окнами (рис. 4.1.3-1). С помощью этих команд можно упорядочивать, скрывать окна и переходить из одного окна в другое. Кроме того, команды данного меню позволяют активизировать любое открытое окно.
Элемент главного меню Справка ( Help )использует свои команды для вызова справочной системы с различными вариантами представления информации.
4.1.4. Стандартная панель инструментов и
окна панелей VS
В VSсодержится большое количество панелей инструментов для отладки и запуска программ, задания расположения элементов на форме и многого другого. Познакомимся со стандартной панелью инструментов (рис. 4.1.4-1), которая используется во всех режимах работы.
Рис. 4.1.4-1. Стандартная панель инструментов
По умолчанию в главном окне программы VisualBasic всегда присутствует стандартная панель инструментов, если только вы не удалили ее с экрана. Если для работы необходима стандартная панель инструментов, выберите команду Стандартная ( Standard)элемента меню Панели инструментов ( Toolbars) из элемента главного меню Вид (View).
Окнопанели Начальная страница ( StartPage ) позволяет просмотреть последние использовавшиеся проекты, осуществить поиск примеров программ, как из справки, так и Интернета, а также просмотреть ссылки на сайты, содержащие новости о продукте VS, документацию, учебные пособия.
Начальная страница автоматически открывается при запуске VS. Если же окно Начальная страница( Start Page)не появилось, его можно вызвать с помощью команды Начальная страница ( Start Page) элемента главного меню Вид( View).
Окно панели Конструктор ( Designer )является основным рабочим окном, в котором выполняется визуальное проектирование приложения. Вызвать это окно можно из главного меню командой Конструктор( Designed) элемента главного меню Вид ( View)или двойным щелчком на названии формы в окне Обозреватель решений ( Solution Explorer).
В Окне конструктора форм визуально создаются все формы приложения с использованием инструментария среды разработки. Для точного позиционирования объектов на форме в окне можно использовать сетку.
Размер формы в окне можно изменять, используя маркеры выделения формы и мышь. Для изменения размера формы необходимо установить указатель мыши на маркер и, когда он примет вид двунаправленной стрелки,.
Для работы в окне Конструктор можно использовать контекстное меню.
Окно панели Редактор кода (CodeEditor)–это мощный текстовый редактор с большим количеством возможностей, являющийся основным инструментом программиста для создания и отладки приложения.
В окне панели Редактор кода (CodeEditor)(рис.4.1.6-2)расположены элементы:
· раскрывающийся список Имя класса (Class Name) содержит перечень объектов приложения; при выборе объекта в этом списке содержимое списка Имя метода (Method Name) изменяется;
· раскрывающийся список Имя Метода (Method Name) дает возможность выбора членов объекта (событий) и автоматического вывода в окно редактора процедуры или шаблона для выбранного члена.
Для работы в окне редактора можно использовать контекстное меню.
Окно панели Обозреватель решений ( SolutionExplorer )позволяет получить доступ к компонентам, входящим в проект. Если после создания или открытия проектаэтого окна нет на экране, то можно его отобразить одним из следующих способов:
· вэлементе главного меню Вид ( View) выбрать команду Обозреватель решений ( SolutionExplorer);
· нажать кнопку Обозреватель решений ( SolutionExplorer) стандартной панелиинструментов;
· нажать комбинацию клавиш < Ctrl>+< Alt>+< L>.
Окно панели Обозреватель решений ( SolutionExplorer) содержит список всех компонентов решения (проекта). В верхней части окна размещается имя решения, а ниже располагаются входящие в него проекты и файлы. Также окно панели Обозреватель решений ( SolutionExplorer) содержит кнопки, отображение которых зависит от типа, выделенного в окне компонента.
Для работы в окне Обозреватель решений ( SolutionExplorer) можно пользовать контекстное меню.
Окно панели Панель элементов (Toolbox) - основной рабочий инструмент при визуальной разработке форм приложения.Панель элементов управления вызывается из главного менюВид (View) командой Панель элементов (Toolbox) или нажатием кнопки Панель элементов (Toolbox) на стандартной панели инструментов.
Окно панели Свойства ( Properties )предназначено для отображения и настройки свойств объектов проекта, включая форму и размещенные в ней объекты. В этом окне, например, содержатся такие свойства выбранного объекта, как позиция в форме, высота, ширина, цвет.
Для открытия диалогового окна Свойства (Properties) следует выполнить одно из следующих действий:
· в элементе главного меню Вид ( View) выбрать команду Окносвойств ( PropertiesWindow);
· нажать кнопку Окносвойств ( PropertiesWindow), расположенную настандартной панели инструментов;
· выбрать команду Свойства ( Properties) контекстного меню выделенного объекта;
· нажать клавишу <F4>.
Поскольку форма и элементы управления также являются объектами, то набор свойств в этом окне меняется в зависимости от выбранного объекта. С помощью кнопок
Валфавитном порядке( Alphabetical)и ПоКатегориям( Categorized)свойства объекта можно просмотреть в алфавитном порядке или по группам (категориям).
В нижней части окна появляется подсказка, поясняющая назначение выбранного свойства объекта. Более подробное пояснение можно посмотреть в справочной системе. Также можно воспользоваться динамической справкой, выбрав нужный раздел в окне Динамическая справка( DynamicHelp).
Используя диалоговое окно Свойства ( Properties), можно изменить установленные по умолчанию свойства объектов. Часть свойств объекта, например, размеры и расположение, можно задать перемещением объекта и изменением его размеров с помощью мыши в конструкторе форм. Свойства, установленные в окне свойств, можно изменять при выполнении приложения, написав соответствующие коды в процедурах, создаваемых с помощью редактора кода.
Как правило, форма содержит много объектов. Если выбрать сразу несколько объектов, то в окне свойств можно увидеть общие для этих объектов свойства.
Окно панелиОбозреватель объектов ( ObjectBrowser )предназначено для просмотра всех объектов, входящих в состав проекта. Вэтом окне можно получить доступ не только ко всем входящим в проект объектам, но и их свойствам, методам, событиям. Окно просмотра объектов обычно не визуализировано, и его можно вызвать командой Обозреватель объектов ( ObjectBrowser) из меню Вид ( View).
С помощью раскрывающегося списка Обзор( Browse) окна Обозреватель объектов ( ObjectBrowser) задается область просмотра.
Этот список может содержать следующие значения:
· Все компоненты (All Components) - в окнеObject Browser (Обозреватель
объектов)будутотображаться все имеющиеся компоненты;
· Компоненты платформы . NETFramework (. NETFramework) - окно просмотра
объектов будет содержать все библиотеки классов платформы . NETFramework;
· Мое решение ( MySolution) – в окне ObjectBrowser
(Обозреватель объектов) будут отображаться содержащиеся в открытом решении пространства имен, классы, структуры, интерфейсы, типы и их свойства, методы, события, переменные, константы;
· Настраиваемый набор компонент ( CustomComponentSet) - в окне
просмотра объектов будет отображаться содержимое компонентов, указанных в диалоговом окнеИзменить настраиваемый объект компонентов ( EditCustomComponentSet), которое открывается с помощью расположеннойсправа от списка кнопки Добавить (Add). В
качестве компонентов могут выступать проекты решения, СОМ-объекты, внешние библиотеки и исполняемые файлы.
Для задания типа отображаемых компонентов предназначена кнопка Обозреватель объектов: параметры( ObjectBrowser Settings), при нажатии на которую открывается меню со списком типов компонентов: пространства имен, контейнеры, базовые, производные и скрытые типы, открытые, защищенные и скрытые члены классов. При выборе типа слева от его наименования появляется галочка.
Для работы в окне Обозреватель объектов ( ObjectBrowser) можно использовать контекстное меню.
Окно панелиЛокальные ( Locals ) предназначено для просмотра списка локальных переменных приложения и контроля их значений. Вызвать его можно подкомандой Локальные (Locals) команды Окна ( Windows) элемента основного меню Отладка (Debug)или из панели элементов Отладка (Debug).
В окне Локальные (Locals)удобно просматривать имена локальных переменных, объявленных в текущей процедуре, тип и значения этих переменных. Указанная информация появляется в окне автоматически при его вызове. Данное окно используется для отладки и проверки работы приложения. Окно панелиВидимые ( Watch ) предназначенодля более полногоконтроля работы приложения. Это окно вызывается подкомандой Видимые ( Watch) команды Окна ( Windows) элемента основного меню Отладка (Debug)или из панели элементов Отладка (Debug).
Тема 4.2. Основные средства языка программированияC#
4.2.1. Синтаксис, семантика, алфавит и лексемы языка
4.2.2. Типы данных
4.2.3. Переменные, выражения и основные операции C#
4.2.4. Математические функции класса Math и функции класса Random
4.2.5. Преобразование данных в математических выражениях и операторе
присваивания
4.2.6. Выражения над строками и преобразование строк
4.2.1. Синтаксис, семантика, алфавит и лексемы
языка
Содержательно язык программирования – это средство общения между человеком (программистом) и компьютером (исполнителем). Рассматривая любую знаковую систему (в том числе и язык программирования), обычно выделяют синтаксис- правила построения сообщений в этой системе, семантику– правила истолкования сообщений тем, кому они адресованы, а также прагматику, сопоставляющую сообщения желаниям того, от кого они исходят.
Всякий язык программирования, в частности, C #, можно определить как множество предложений (строк) – т.е. некоторое множество конечных последовательностей элементарных единиц из некоторого непустого конечного множества символов (алфавита), называемого словарем языка. Понятно, что при таком рассмотрении языка программирования мы только фиксируем множество символов, которые можно использовать для записи программ, а также класс допустимых(или, как принято говорить, синтаксически правильных) программ, не сопоставляя никакого смысла этим синтаксически правильным программам.
Ясно, что задавать множество допустимых программ исчерпывающим их перечислением невозможно. Желательно, чтобы описание языка было обозримым (заведомо конечным), хотя описываемый язык может быть и бесконечным. Обычный подход, удовлетворяющий этому требованию, состоит в том, что предложения (строки) языка строятся по определенным правилам, в совокупности составляющим то, что называют грамматикой языка. Эти грамматические правила приписывают предложениям языка некоторую синтаксическую структуру, которая используется в дальнейшем при определении смысла предложений.
Грамматика языка программирования может быть описана различными способами. Например, ее можно задавать в виде порождающей системы формальных и неформальных правил, т.е. набора правил, применением которых можно породить все предложения языка. При описании конструкций языка C # мы будем применять способ, использующий неформальные правила описания предложений языка C #.
Семантика языка программирования – это правила придания смысла синтаксически правильным программам. В конечном счете, эти правила определяют ту последовательность действий вычислительной машины, которую она должна выполнить, работая по данной программе. Например, семантика языка команд компьютера определяется самим компьютером: машинная программа описывает в точности то, что реализует вычислительная машина при работе по данной программе.
Алфавит языка программированияиспользует для записи программ сравнительно небольшое множество литер (символов), доступных на устройствах ввода-вывода реальных ЭВМ. То есть алфавит (или множество литер) языка программированияС# составляют символы таблицы кодов Unicode.Кодировка Unicode позволяет представить символы всех существующих алфавитов одновременно. Каждому символу соответствует свой код.
Предполагается, что входной алфавит содержит буквы русского и латинского алфавитов, десятичные арабские цифры и некоторое количество специальных литер, таких как пробел, точка, запятая и т.п.
Таким образом, в алфавит языка программирования C# входят:
· строчные и прописные буквы (латинские и национальных алфавитов)и символ
· подчеркивания;
· цифры от 0 до 9;
· пробельные символы (пробел и символы табуляции);
· символы перевода строки.
Для записи даже элементарных конструкций языка C# используются не отдельные литеры (символы) входного алфавита, а слова, называемые лексемами. При этом символы, образующие лексему, теряют свою индивидуальность: смысловое значение, приписываемое лексеме семантикой, не обязано вытекать из смысловых значений составляющих ее символов.
Множество лексем образует словарь языка. Он включает в себя следующие пять групп лексем:
· идентификаторы;
· ключевые слова;
· знаки (символы) операций;
· литералы;
· разделители.
Лексемы это минимальные единица языка, имеющие самостоятельный смысл и аналогичны словам естественного языка. Например, лексемами являются число 128 (но не его часть 12), имяVasia, ключевое слово for и знак операции сложения +.
Комментарии предназначены для записи пояснений к программе и формирования документации. Правила записи комментариев мы рассмотрим чуть позже.
Из лексем составляются выражения и операторы. Выражение задает правило вычисления некоторого значения. Например, выражение a + b задает правило вычисления суммы двух величин.
Оператор задает законченное описание некоторого действия, данных или элемента программы. Например:int a;.Это– оператор описания целочисленной переменной a.
Почти все типы лексем (кроме ключевых слов и идентификаторов) имеют собственные правила словообразования, включая собственные подмножества алфавита.
Лексемы обособляются разделителями. Этой же цели служит множество пробельных символов, к числу которых относятся пробел, табуляция, символ новой строки и комментарии.
Рассмотрим правила использования некоторых лексических элементов.
Идентификаторы– это имена лексического элемента языка программирования. Для выбора имени лексического элемента языка следует придерживаться следующих правил:
· первым символом идентификатора С# может быть только буква;
· следующими символами идентификатора могут быть буквы, цифры;
· длина идентификатора не ограничена.
В идентификаторах C# разрешается использовать, помимо латинских букв, буквы национальных алфавитов. По правилам хорошего стиля программирования имя обязано быть ясным, легко воспринимаемым и при этом как можно более точно отражать смысл и назначение именуемой величины.
Имена даются элементам программы, к которым требуется обращаться: переменным, типам, константам, методам, меткам и т. д. Идентификатор создается на этапе объявления переменной (метода, типа и т. п.), после этого его можно использовать в последующих операторах программы. При выборе идентификатора необходимо следить, чтобы он не совпадал с ключевыми словами.
Имена – это идентификаторы. Любая случайным образом составленная последовательность букв, цифр и знаков подчеркивания с точки зрения грамматики языка идеально подходит на роль имени любого объекта, если только начинается с буквы. Фрагмент программы, содержащий подобную переменную, будет синтаксически безупречен.
Ключевые слова– это зарезервированные идентификаторы, которые имеют специальное значение для компилятора. Их можно использовать только в том смысле, в котором они определены. Список ключевых слов C# приведен в табл.4.2.1-1.
Таблица 4.2.1-1. Ключевые слова C# | |||||||
abstract | as | base | bool | break | byte | case | catch |
char | checked | class | const | continue | decimal | default | delegate |
do | double | пelse | enum | false | finally | fixed | float |
for | foreach | goto | if | implicit | in | int | interface |
internal | is | lock | long | namespace | new | null | object |
operator | out | override | params | private | protected | public | readonly |
ref | return | sbyte | sealed | short | sizeof | stackalloc | static |
string | struct | switch | this | throw | true | try | typeof |
uint | ulong | unchecked | unsafe | ushort | using | virtual | void |
volatile | while |
Комментарии– это пояснения отдельных конструкций и их действий в программе. При выполнении программы C#игнорирует комментарии. По умолчанию при выполнении приложения комментарий выделяется зеленым цветом.
Часто бывает полезно вставлять в программу текст, который является комментарием только для читающего программу человека и игнорируется компилятором. В языкеС#это можно сделать одним из двух способов.
1. Символы /* начинают комментарий, заканчивающийся символами */.
Это особенно полезно для многострочных комментариев и изъятия частей программы при редактировании, однако следует помнить, что комментарии /* */ не могут быть вложенными.
2. Символы // начинают комментарий, заканчивающийся в конце строки, на которой они появились. Этот способ наиболее полезен для коротких комментариев. Символы // можно использовать для того, чтобы закомментировать символы /* или */, а символами /* можно закомментировать //.
Литералы – это особая категория слов языка. Их существует четыре типа:
· целочисленный литерал;
· вещественный литерал;
· символьный литерал;
· строковый литерал.
Для каждого подмножества литералов используются собственные правила словообразования.
Целочисленный литерал служит для записи целочисленных значений и является соответствующей последовательностью цифр (возможно, со знаком-). Целочисленный литерал, начинающийся со знака O, воспринимается как восьмеричное целое. В этом случае цифры 8 и 9 не должны встречаться среди составляющих литерал символов. Целочисленный литерал, начинающийся сОх или ОХ, воспринимается как шестнадцатеричное целое. В этом случае целочисленный литерал может включать символы отА или а, до F или f, которые в шестнадцатеричной системе эквивалентны десятичным значениям от 10 до 15. Непосредственно за литералом могут располагаться в произвольном сочетании один или два специальных суффикса: U (или u) и L (или l).
Вещественный литерал служит для отображения вещественных значений. Он фиксирует запись соответствующего значения в обычной десятичной или научной нотациях. В научной нотации мантисса отделяется от порядка литеройЕ (или е). Непосредственно за литералом может располагаться один из двух специальных суффиксов: F (или f) и L (или l).
Значением символьного литерала является соответствующее значение ASCII кода (это, разумеется, не только буквы, буквы-цифры или специальные символы алфавитаС#).
Символьный литерал представляет собой последовательность одной или нескольких литер, заключенных в одинарные кавычки. Символьный литерал служит для представления литер в одном из форматов представления. Например, литера Z может быть представлена литералом «Z», а также литералами «\132» и «\х5А». Любая литера может быть представлена в нескольких форматах представления: обычном, восьмеричном и шестнадцатеричном.
Строковые литералы являются последовательностью (возможно, пустой) литер в одном из возможных форматов представления, заключенных в двойные кавычки. Строковые литералы, расположенные последовательно, соединяются в один литерал, причем литеры соединенных строк остаются различными. Так, последовательность строковых литералов «\xF» «F» после объединения будет содержать две литеры, первая из которых является символьным литералом в шестнадцатеричном формате «\xF», вторая – символьным литералом «F». Строковый литерал и объединенная последовательность строковых литералов заканчиваются пустой литерой, которая используется как индикатор конца литерала.
Примеры, иллюстрирующие наиболее часто употребляемые формы констант, выделены полужирным шрифтом.
Таблица4. 2.1-2. Константы в C# | ||
Константа | Описание | Примеры |
Логическая | true (истина) или false (ложь) | Truefalse |
Целая | Десятичная: последовательность десятичных цифр (0, 1, 2, 3, 4, 5, 6, 7, 8, 9), за которой может следовать суффикс (U, u, L, l, UL, Ul, uL, ul, LU, Lu, lU, lu) | 8 0 199226 8u 0Lu 199226L |
Шестнадцатеричная: символы 0х или 0Х, за которыми следуют шестнадцатеричные цифры (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F), а за цифрами, в свою очередь, может следовать суффикс (U, u, L, l, UL, Ul, uL, ul, LU, Lu, lU, lu) | 0xA 0x1B8 0X00FF 0xAU 0x1B8LU 0X00FFl | |
Веществ-ная | С фиксированной точкой: [цифры][.][цифры][суффикс] Суффикс — один из символов F, f, D, d, M, m | 5.7 .001 35. 5.7F .001d 35. 5F .001f 35m |
С порядком: [цифры][.][цифры]{E|e}[+|–][цифры] [суффикс] Суффикс — один из символов F, f, D, d, M, m | 0.2E6 .11e+3 5E-10 0.2E6D .11e–3 5E10 | |
Символьная | Символ, заключенный в апострофы | 'A' 'ю' '*''\0' '\n' '\xF' '\x74''\uA81B' |
Строковая | Последовательность символов, заключенная в кавычки | "Здесь был Vasia" "\tЗначение r = \xF5 \n" "Здесь был \u0056\u0061" "C:\\temp\\file1.txt" @"C:\temp\file1.txt" |
Константа null | Ссылка, которая не указывает ни на какой объект | null |
Логических литералов всего два: true и false. Они широко используются в качестве признаков наличия или отсутствия чего-либо.
Целые литералы могут быть представлены либо в десятичной, либо в шестнадцатеричной системе счисления, а вещественные – только в десятичной системе, но в двух формах: с фиксированной точкой и с порядком. Вещественная константа с порядком представляется в виде мантиссы и порядка. Мантисса записывается слева от знака экспоненты (E или e), порядок — справа от знака. Значение константы определяется как произведение мантиссы и возведенного в указанную в порядке степень числа 10 (например, 1.3e2 = 1,3 × 102 = 130). Пробелы внутри константы не допускаются.
Если требуется сформировать отрицательную целую или вещественную константу, то перед ней ставится знак унарной операции изменения знака (–), например, –218.
Символьная константа представляет собой любой символ в кодировке Unicode. Символьные константы записываются в одной из четырех форм:
· "обычный" символ, имеющий графическое представление (кроме апострофа и символа перевода строки) — 'A', 'ю', '*';
· управляющая последовательность — '\0', '\n';
· символ в виде шестнадцатеричного кода — '\xF', '\x74';
· символ в виде escape-последовательности Unicode — '\uA81B'.
Управляющей последовательностью, или простой escape-последовательностью, называют определенный символ, предваряемый обратной косой чертой. Управляющая последовательность интерпретируется как одиночный символ и используется для:
· кодов, не имеющих графического изображения (например, \n — переход в начало следующей строки);
· символов, имеющих специальное значение в строковых и символьных литералах, например, апострофа ( ' ).
Допустимые значения последовательностей приведены в табл.4.2.1-3.
Таблица 4.2.1-3. Управляющие последовательности в С# | |
Вид | Наименование |
\a | Звуковой сигнал |
\b | Возврат на шаг |
\f | Перевод страницы (формата) |
\n | Перевод строки |
\r | Возврат каретки |
\t | Горизонтальная табуляция |
\v | Вертикальная табуляция |
\\ | Обратная косая черта |
\' | Апостроф |
\" | Кавычка |
\0 | Нуль-символ |
Символ, представленный в виде шестнадцатеричного кода, начинается с префикса \x, за которым следует код символа. Числовое значение должно находиться в диапазоне от 0 до 216 – 1, иначе возникает ошибка компиляции.
Константа null представляет собой значение, задаваемое по умолчанию, для величин так называемых ссылочных типов, которые мы рассмотрим далее в этой лекции.
Операторыязыка программирования– это последовательности команд, из которых состоит код программы (текст программы). Оператор– это синтаксическая единица языка программирования, которая определяет некоторое действие. Операторы описывают алгоритмические действия, которые должны выполняться для решения поставленной задачи. Каждый оператор представляет собой законченную фразу (предложение, строку) языка программирования и может содержать ключевые слова, переменные, константы, выражения, разделители др. Операторы могут быть простыми и структурированными. Простым оператором является такой оператор, который не содержит в себе других операторов. Структурированные операторы строятся из других операторов, порядок выполнения которых должен быть последовательным, определяемым условной передачей управления (условными операторами) или повторяющимся (определяется операторами цикла).
Данные– это информация, представленная в формальном виде, который обеспечивает возможность ее хранения, обработки и передачи (смотри введение к пособию по Информатике). В основе любого языка программирования лежит конечное множество данных, не делимых (простых) с точки зрения имеющихся в языке операций. Это множество распадается на четыре подмножества, состоящих соответственно из целых чисел, вещественных чисел, логических значений и литерных (строковых) значений. Каждое из перечисленных подмножеств – это стандартный тип данных, который имеет свое имя (идентификатор), характеризуется множеством значений и набором операций, определяемых языком программирования над этими значениями (например, числа можно складывать, а логические или литерные значения – нет). Как известно, данные в программе могут быть двух видов: постоянные величины (константы) и переменные величины (переменные). Предполагается, что каждая переменная в программе (переменная – это элемент памяти ЭВМ; адрес элемента памяти – это имя переменной; а его содержимое – значение переменной) может получать значения только определенного заранее типа, в то время как элементы памяти ЭВМ могут хранить любое по типу значение – его трактовка определяется только выполняемой операцией. Помимо переменных программа может содержать константы – элементы памяти ЭВМ, которые постоянно хранят заданные значения. В C#значение константы может использоваться двумя способами: или неявно, своим именем – адресом соответствующего элемента памяти, или явно, изображением значения содержимого соответствующего элемента памяти. Например, True– это имя значения "истина", а 125– изображение значения числа "сто двадцать пять".
Знак операции– это один или более символов, определяющих действие над операндами. Внутри знака операции пробелы не допускаются. Например, в выражении a += b знак += является знаком операции, а a и b — операндами. Символы, составляющие знак операций, могут быть специальными, например, +, &&, | и <, и буквенными, такими как as или new.
Операции делятся на унарные, бинарные и тернарную по количеству участвующих в них операндов (один, два и три операнда соответственно). Один и тот же знак может интерпретироваться по-разному в зависимости от контекста.
Разделители используются для разделения или, наоборот, группирования элементов. Примеры разделителей: скобки, точка, запятая. Ниже перечислены все знаки операций и разделители, использующиеся в C#:
{ } [ ] ( ) . , : ; + - * / % & | ^ ! ~ =
<> ? ++ -- && || <<>> == != <= >= += -= *= /= %=
&= |= ^= <<= >>= ->
программы, проверяет их соответствие правилам и записывает их в отдельный файл.
Типы данных
Все типы C# можно разделить на четыре категории:
· Типы-значения (value), или значимые типы;
· Ссылочные (reference);
· Указатели (pointer);
· Пустой тип (void).
Эта классификация основана на том, где и как хранятся значения типов.Переменные, или что тоже в данном контексте – объекты, хранят свои значения в памяти компьютера, которую будем называть памятью типа Стек (Stack).Другой вид памяти, также связанный с хранением значений переменной будем называть памятью типа Куча (Heap). Для значимого типа значение переменной хранится непосредственно в стеке. Поскольку значение может быть сложным и состоять, например, из множества скалярных значений, то говорят, что значение разворачивается в стеке.По этой причине значимый тип называют также развернутым типом.Для ссылочного типа значение в стеке задает ссылку на область памяти в «куче», где хранятся собственно данные, задающие значение.Данные, хранящиеся в куче, в этом случае называют объектом, а значение, хранящееся в стеке, ссылкой на объект.Самое важное в этой модели хранения значений это то, что разные ссылки в стеке могут указывать на один и тот же объект из кучи.И тогда у этого объекта есть много разных имен (псевдонимов), каждое из которых позволяет получить доступ к полям объекта и изменять хранящиеся там значения.
В отдельную категорию выделены указатели, что подчеркивает их особую роль в языке.Указатели и ссылки в языке C# хотя и возможны, но в большинстве проектов используются редко.Отказ от этих средств делает программы более простыми, а самое главное более надежными.Особый статус имеет и тип void, указывающий на отсутствие какого-либо значения.
В языке C# жестко определено, какие типы относятся к ссылочным, а какие к значимым.Типы – логический, арифметический, структуры, перечисление – относятся к значимым типам.Массивы, строки и классы относятся к ссылочным типам.На первый взгляд, такая классификация может вызывать некоторое недоумение, почему это структуры относятся к значимым типам, а массивы и строки – к ссылочным.Однако ничего удивительного здесь нет.В C# массивы рассматриваются как динамические, их размер может определяться на этапе вычислений, а не в момент трансляции. Поэтому естественно хранить массивы в динамической памяти – куче, а не в статической памяти, каковой является стек, где размеры хранимых данных не меняются в процессе выполнения. Строки в C# также рассматриваются как динамические переменные, длина которых может изменяться.Поэтому строки и массивы относятся к ссылочным типам, требующим распределения памяти в куче.
Со структурами дело сложнее.Структуры C# представляют частный случай класса.Два объявления типа данных могут отличаться лишь одним ключевым словом, начинающим это объявление – class или struct. В зависимости от того, какое ключевое слово использовано, данное объявление будет задавать класс (ссылочный тип) или структуру (значимый тип). Определив тип как структуру, программист получает возможность отнести класс к значимым типам, что иногда бывает крайне полезно.У программиста C# только благодаря структурам появляется возможность управлять отнесением класса к значимым или ссылочным типам.Правда, это не совсем полноценное средство, поскольку на структуры накладываются дополнительные и довольно жесткие ограничения по сравнению с обычными классами.В частности, для структур разрешено только наследование интерфейсов и структура не может иметь в качестве родителя класс или структуру.Все развернутые типы языка C# - int, double и прочие реализованы как структуры.
Все базисные встроенные типы C# однозначно отображаются, а фактически совпадают с системными типами каркаса .Net Framework, размещенными в пространстве имен System.Поэтому всюду, где можно использовать имя типа, например, – int, с тем же успехом можно использовать и имя – System.Int32.
Тип данных однозначно определяет:
· внутреннее представление данных, а, следовательно, и множество их возможных значений;
· допустимые действия над данными (операции и функции).
Например, целые и вещественные числа, даже если они занимают одинаковый объем памяти, имеют совершенно разные диапазоны возможных значений.
Каждое выражение в программе имеет определенный тип. Компилятор использует информацию о типе при проверке допустимости описанных в программе действий.
Память, в которой хранятся данные во время выполнения программы, делится на две области: стек (stack) и динамическая область, или хип (heap), чаще называемый кучей (поскольку этот термин мне не нравится, я его использовать не буду). Стек используется для хранения величин, память под которые выделяет компилятор, а в динамической области память резервируется и освобождается во время выполнения программы с помощью специальных команд. Основным местом для хранения данных в C# является хип.
Типы можно классифицировать по разным признакам. Если принять за основу строение элемента, то все типы можно разделить на простые, не имеющие внутренней структуры, и структурированные, состоящие из элементов других типов. По своему "создателю" типы можно разделить на встроенные (стандартные) и определяемые программистом. По способу хранения значений типы делятся на значимые, или типы-значения, и ссылочные. Рассмотрим в первую очередь встроенные типы C#.
Встроенные типы не требуют предварительного определения. Для каждого типа существует ключевое слово, которое используется при описании переменных, констант и т. д. Встроенные типы C# приведены в табл.4.2.2-1. Они однозначно соответствуют стандартным классам библиотеки .NET, определенным в пространстве имен System. Как видно из таблицы, существуют несколько вариантов представления целых и вещественных величин. Программист выбирает тип каждой величины, используемой в программе, с учетом необходимого ему диапазона и точности представления данных.
Вещественные типы являются знаковыми. Для них в столбце "диапазон значений" приведены абсолютные величины допустимых значений.
Логический, или булев, тип содержит всего два значения: true (истина) и false (ложь). Все целые и вещественные типы вместе с символьным можно назвать арифметическими.
Таблица 4.2.2-1. Встроенные типы C# | |||||
Название | Ключевое слово | Тип .NET | Диапазон значений | Описание | Размер, битов |
Логический тип | bool | Boolean | true, false | ||
Целые типы | sbyte | SByte | От –128 до 127 | Со знаком | 8 |
byte | Byte | От 0 до 255 | Без знака | 8 | |
short | Int16 | От –32768 до 32767 | Со знаком | 16 | |
ushort | UInt16 | От 0 до 65535 | Без знака | 16 | |
int | Int32 | От –2 × 109 до 2 × 109 | Со знаком | 32 | |
uint | UInt32 | От 0 до 4 × 109 | Без знака | 32 | |
long | Int64 | От –9 × 1018 до 9 × 1018 | Со знаком | 64 | |
ulong | UInt64 | От 0 до 18 × 1018 | Без знака | 64 | |
Символьный тип | char | Char | От U+0000 до U+ffff | Unicode-символ | 16 |
Вещественные | float | Single | От 1.5 × 10-45 до 3.4 × 1038 | 7 цифр | 32 |
double | Double | От 5.0 × 10-324 до 1.7 × 10308 | 15–16 цифр | 64 | |
Строковый тип | string | String | Длина ограничена объемом доступной памяти | Строка из Unicode-символов |
Внутреннее представление величины целого типа — целое число в двоичном коде. В знаковых типах старший бит числа интерпретируется как знаковый (0 — положительное число, 1 — отрицательное).
Вещественные типы, или типы данных с плавающей точкой, хранятся в памяти компьютера иначе, чем целочисленные. Внутреннее представление вещественного числа состоит из двух частей — мантиссы и порядка, причем каждая часть имеет знак. Длина мантиссы определяет точность числа, а длина порядка — его диапазон. Все вещественные типы могут представлять как положительные, так и отрицательные числа. Чаще всего в программах используется тип double, поскольку его диапазон и точность покрывают большинство потребностей. Этот тип имеют вещественные литералы и многие стандартные математические функции.
Тип decimal предназначен для денежных вычислений, в которых критичны ошибки округления. Величины типа decimal позволяют хранить 28–29 десятичных разрядов. Тип decimal не относится к вещественным типам, у них различное внутреннее представление. Любой встроенный тип C# соответствует стандартному классу библиотеки .NET, определенному в пространстве имен System. Имя этого класса приведено в третьем столбце таблицы 4.2.2-1. Везде, где используется имя встроенного типа, его можно заменить именем класса библиотеки.
Литералы (константы) тоже имеют тип. Если значение целого литерала находится внутри диапазона допустимых значений типа int, литерал рассматривается как int, иначе он относится к наименьшему из типов uint, long или ulong, в диапазон значений которого он входит. Вещественные литералы по умолчанию относятся к типу double.
Например, константа 10 относится к типу int, хотя для ее хранения достаточно и байта, а константа 2147 483 648 будет определена как uint. Для явного задания типа литерала служит суффикс, например, 1.1f, 1UL, 1000m. Явное задание применяется в основном для уменьшения количества неявных преобразований типа, выполняемых компилятором.
Типы-значения и ссылочные типы. Чаще всего типы C# разделяют по способу хранения элементов на типы-значения и ссылочные типы. Элементы типов-значений, или значимых типов (value types), представляют собой просто последовательность битов в памяти, необходимый объем которой выделяет компилятор. Иными словами, величины значимых типов хранят свои значения непосредственно. Величина ссылочного типа хранит не сами данные, а ссылку на них (адрес, по которому расположены данные). Сами данные хранятся в куче.
4.2.3. Переменные, выражения и основные операции C#
Переменная –это именованная область памяти, предназначенная для хранения данных определенного типа. Во время выполнения программы значение переменной можно изменять. Все переменные, используемые в программе, должны быть описаны явным образом. При описании для каждой переменной задаются ее имя и тип.
Пример описания целой переменной с именем a и вещественной переменной x:
int a; float x;
Имя переменной служит для обращения к области памяти, в которой хранится значение переменной. Имя дает программист. Тип переменной выбирается, исходя из диапазона и требуемой точности представления данных. При объявлении можно присвоить переменной некоторое начальное значение, то есть инициализировать ее, например:
int a, b = 1;
float x = 0.1, y = 0.1f;
Здесь описаны:
· переменная a типа int, начальное значение которой не присваивается;
· переменная b типа int, ее начальное значение равно 1;
· переменные х и y типа float, которым присвоены одинаковые начальные значения 0.1. Разница между ними состоит в том, что для инициализации переменной х сначала формируется константа типа double, а затем она преобразуется к типу float; переменной y значение 0.1 присваивается без промежуточного преобразования.
Рекомендуется всегда инициализировать переменные при описании. При инициализации можно использовать не только константу, но и выражение — главное, чтобы на момент описания оно было вычисляемым, например:
int b = 1, a = 100;
int x = b * a + 25;
Можно запретить изменять значение переменной, задав при ее описании ключевое слово const, например:
const int b = 1;
const float x = 0.1, y = 0.1f;// const распространяется на обе переменные
Такие величины называют именованными константами, или просто константами. Они применяются для того, чтобы вместо значений констант можно было использовать в программе их имена. Это делает программу более понятной и облегчает внесение в нее изменений. Именованные константы должны обязательно инициализироваться при описании, например:
const int b = 1, a = 100;
const int x = b * a + 25;
Выражение – это правило вычисления значения. В выражении участвуют операнды, объединенные знаками операций. Операндами простейшего выражения могут быть константы, переменные и вызовы функций.
Например, a + 2 – это выражение, в котором + является знаком операции, а a и 2 – операндами. Пробелы внутри знака операции, состоящей из нескольких символов, не допускаются. Операции C# приведены в таб. 4.2.3-1.
Таблица 4.2.3-1. Операции C# | ||
Категория | Знак операции | Название |
Первичные | Доступ к элементу | |
x() | Вызов метода или делегата | |
x[] | Доступ к элементу | |
x++ | Постфиксный инкремент | |
x-- | Постфиксный декремент | |
new | Выделение памяти | |
typeof | Получение типа | |
checked | Проверяемый код | |
unchecked | Непроверяемый код | |
Унарные | + | Унарный плюс |
- | Унарный минус | |
! | Логическое отрицание | |
˜ | Поразрядное отрицание | |
++x | Префиксный инкремент | |
--x | Префиксный декремент | |
(тип)x | Преобразование типа | |
Мультипликативные (типа умножения) | * | Умножение |
/ | Деление | |
% | Остаток от деления | |
Аддитивные (типа сложения) | + | Сложение |
- | Вычитание | |
Сдвига | << | Сдвиг влево |
>> | Сдвиг вправо | |
Отношения и проверки типа | < | Меньше |
> | Больше | |
<= | Меньше или равно | |
>= | Больше или равно | |
is | Проверка принадлежности типу | |
as | Приведение типа | |
Проверки на равенство | == | Равно |
!= | Не равно | |
Условные логические | && | Логическое И |
|| | Логическое ИЛИ | |
Условная | ? : | Условная операция |
Присваивания | = | Присваивание |
*= | Умножение с присваиванием | |
/= | Деление с присваиванием | |
%= | Остаток отделения с присваиванием | |
+= | Сложение с присваиванием | |
-= | Вычитание с присваиванием | |
<<= | Сдвиг влево с присваиванием | |
>>= | Сдвиг вправо с присваиванием | |
&= | Поразрядное И с присваиванием | |
= | Поразрядное исключающее ИЛИ с присваиванием | |
|= | Поразрядное ИЛИ с присваиванием |
Операции в выражении выполняются в определенном порядке в соответствии с приоритетами, как и в математике. В таб. 4.2.3-1операции расположены по убыванию приоритетов, уровни приоритетов разделены в таблице горизонтальными линиями.
Результат вычисления выражения характеризуется значением и типом. Например, пусть a и b — переменные целого типа и описаны так:
int a = 2, b = 5;
Тогда выражение a + b имеет значение 7 и тип int, а выражение a = b имеет значение, равное помещенному в переменную a (в данному случае – 5) и тип, совпадающий с типом этой переменной.
Если в одном выражении соседствует несколько операций одинакового приоритета, операции присваивания и условная операция выполняются справа налево, остальные — слева направо. Для изменения порядка выполнения операций используются круглые скобки, уровень их вложенности практически не ограничен.
Например, a + b + c означает (a + b) + c, а a = b = c означает a = (b = c).
При вычислении выражений может возникнуть необходимость в преобразовании типов. Если операнды, входящие в выражение, одного типа, и операция для этого типа определена, то результат выражения будет иметь тот же тип.
Если операнды разного типа и (или) операция для этого типа не определена, перед вычислениями автоматически выполняется преобразование типа по правилам, обеспечивающим приведение более коротких типов к более длинным для сохранения значимости и точности. Автоматическое (неявное) преобразование возможно не всегда, а только если при этом не может случиться потеря значимости.
Если неявного преобразования из одного типа в другой не существует, программист может задать явное преобразование типа с помощью операции (тип)x. Его результат остается на совести программиста.
Арифметические операции не определены для более коротких, чем int, типов. Это означает, что если в выражении участвуют только величины типов sbyte, byte, short и ushort, перед выполнением операции они будут преобразованы в int. Таким образом, результат любой арифметической операции имеет тип не менее int.
Рис. 4.2.3-1. Неявные арифметические преобразования типов
Правила неявного преобразования иллюстрирует рис. 4.2.3-1. Если один из операндов имеет тип, изображенный на более низком уровне, чем другой, то он приводится к типу второго операнда при наличии пути между ними. Если пути нет, возникает ошибка компиляции.
При вычислении выражений могут возникнуть ошибки, например, переполнение, исчезновение порядка или деление на ноль. В C# есть механизм, который позволяет обрабатывать подобные ошибки и таким образом избегать аварийного завершения программы. Он так и называется: механизм обработки исключительных ситуаций (исключений).
Если в процессе вычислений возникла ошибка, система сигнализирует об этом с помощью специального действия, называемого выбрасыванием (генерированием) исключения. Каждому типу ошибки соответствует свое исключение. Поскольку C# – язык объектно-ориентированный, исключения являются классами, которые имеют общего предка – класс Exception, определенный в пространстве имен System.
Например, при делении на ноль будет сгенерировано исключение DivideByZeroException, при недостатке памяти — исключение OutOfMemoryException.
Обработка исключений подробно рассматривается позже.
Кратко опишем синтаксис и применение всех операций C#, кроме некоторых первичных, которые рассматриваются позже при изучении соответствующего материала.
Операции инкремента (++) и декремента (--) увеличивают и уменьшают операнд на единицу. Эти операции имеют две формы записи –префиксную, когда операция записывается перед операндом, и постфиксную – операция записывается после операнда. Префиксная операция инкремента (декремента) увеличивает (уменьшает) свой операнд и возвращает измененное значение как результат. Постфиксные версии инкремента и декремента возвращают первоначальное значение операнда, а затем изменяют его.
Рассмотрим эти операции на примере.
static void Main() { int i = 3, j = 4; Console.WriteLine("{0} {1}", i, j); Console.WriteLine("{0} {1}", ++i, --j); Console.WriteLine("{0} {1}", i++, j--); Console.WriteLine("{0} {1}", i, j); } | Результат работы: \ 3 4 4 3 4 3 5 2 |
Операция new служит для создания нового объекта;
new тип ( [ аргументы ] )
С помощью этой операции можно создавать объекты как ссылочных, так и значимых типов:
object z = new object();
int i = new int(); // тожесамое, что int i = 0;
При выполнении операции new сначала выделяется необходимый объем памяти (для ссылочных типов в хипе, для значимых – в стеке), а затем вызывается так называемый конструктор по умолчанию, то есть метод, с помощью которого инициализируется объект. Переменной значимого типа присваивается значение по умолчанию, которое равно нулю соответствующего типа.
Операция арифметическое отрицание (унарный минус –) меняет знак операнда на противоположный. Стандартная операция отрицания определена для типов int, long, float, double и decimal. К величинам других типов ее можно применять, если для них возможно неявное преобразование к этим типам.
Логическое отрицание (!) определено для типа bool. Результат операции – значение false, если операнд равен true, и значение true, если операнд равен false.
Операция явное преобразование типа используется для явного преобразования величины из одного типа в другой. Это требуется в том случае, когда неявного преобразования не существует. При преобразовании из более длинного типа в более короткий возможна потеря информации. Формат операции:
(тип) выражение
Здесь тип – это имя того типа, в который осуществляется преобразование, а выражение чаще всего представляет собой имя переменной, например:
int a = (int) b; // данные не теряются
byte d = (byte) a; // данные теряются
Операция умножения (*)возвращает результат перемножения двух операндов. Стандартная операция умножения определена для типов int, uint, long, ulong, float, double и decimal. К величинам других типов ее можно применять, если для них возможно неявное преобразование к этим типам. Тип результата операции равен "наибольшему" из типов операндов, но не менее int.
Все возможные значения для вещественных операндов приведены в таб. 4.2.4-1. Символами х и y обозначены конечные положительные значения, символом z — результат операции вещественного умножения. Если результат слишком велик для представления с помощью заданного типа, он принимается равным значению "бесконечность", если слишком мал, он принимается за 0. NaN (not a number) означает, что результат не является числом.
Таблица 4.2.4-1. Результаты вещественного умножения | |||||||
* | +y | -y | +0 | -0 | +∞ | -∞ | NaN |
+x | +z | -z | +0 | -0 | +∞ | -∞ | NaN |
-x | -z | +z | -0 | +0 | -∞ | +∞ | NaN |
+0 | +0 | -0 | +0 | -0 | NaN | NaN | NaN |
-0 | -0 | +0 | -0 | +0 | NaN | NaN | NaN |
+∞ | +∞ | -∞ | NaN | NaN | +∞ | -∞ | NaN |
-∞ | -∞ | +∞ | NaN | NaN | -∞ | +∞ | NaN |
NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
Операция деления (/)вычисляет частное от деления первого операнда на второй. Стандартная операция деления определена для типов int, uint, long, ulong, float, double и decimal. К величинам других типов ее можно применять, если для них существует неявное преобразование к этим типам. Тип результата определяется правилами преобразования, но не меньше int.
Если оба операнда целочисленные, результат операции округляется вниз до ближайшего целого числа. Если делитель равен нулю, генерируется исключение System.DivideByZeroException.
Если хотя бы один из операндов вещественный, дробная часть результата деления не отбрасывается, а все возможные значения приведены в таб. 4.2.4-2.
Таблица 4.2.4-2. Результаты вещественного деления | |||||||
/ | +y | -y | +0 | -0 | +∞ | -∞ | NaN |
+x | +z | -z | +∞ | -∞ | +0 | -0 | NaN |
-x | -z | +z | -∞ | +∞ | -0 | +0 | NaN |
+0 | +0 | -0 | NaN | NaN | +0 | -0 | NaN |
-0 | -0 | +0 | NaN | NaN | -0 | +0 | NaN |
+∞ | +∞ | -∞ | +∞ | -∞ | NaN | NaN | NaN |
-∞ | -∞ | +∞ | -∞ | +∞ | NaN | NaN | NaN |
NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
Операция остатка от деления (%)также интерпретируется по-разному для целых, вещественных и финансовых величин. Если оба операнда целочисленные, результат операции вычисляется по формуле x - (x / y) * y. Если делитель равен нулю, генерируется исключение System.DivideByZeroException.
Если хотя бы один из операндов вещественный, результат операции вычисляется по формуле x – n * y, где n — наибольшее целое, меньшее или равное результату деления х на y. Все возможные комбинации значений операндов приведены в таб. 4.2.4-3.
Таблица 4.2.4-3. Результаты вещественного остатка от деления | |||||||
% | +y | -y | +0 | -0 | +∞ | -∞ | NaN |
+x | +z | z | NaN | NaN | x | x | NaN |
-x | -z | -z | NaN | NaN | -x | -x | NaN |
+0 | +0 | +0 | NaN | NaN | +0 | +0 | NaN |
-0 | -0 | -0 | NaN | NaN | -0 | -0 | NaN |
+∞ | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
-∞ | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
Операция сложения (+)возвращает сумму двух операндов. Стандартная операция сложения определена для типов int, uint, long, ulong, float, double и decimal. К величинам других типов ее можно применять, если для них существует неявное преобразование к этим типам. Тип результата операции равен "наибольшему" из типов операндов, но не менее int.
Если оба операнда целочисленные или типа decimal и результат операции слишком велик для представления с помощью заданного типа, генерируется исключение System.OverflowException.
Все возможные значения для вещественных операндов приведены в таб. 4.2.4-4.
Таблица 4.2.4-4. Результаты вещественного сложения | ||||||
+ | y | +0 | -0 | +∞ | -∞ | NaN |
x | z | x | x | +∞ | -∞ | NaN |
+0 | y | +0 | +0 | +∞ | -∞ | NaN |
-0 | y | +0 | -0 | +∞ | -∞ | NaN |
+∞ | +∞ | +∞ | +∞ | +∞ | NaN | NaN |
-∞ | -∞ | -∞ | -∞ | NaN | -∞ | NaN |
NaN | NaN | NaN | NaN | NaN | NaN | NaN |
Операция вычитания (-)возвращает разность двух операндов. Стандартная операция вычитания определена для типов int, uint, long, ulong, float, double и decimal. К величинам других типов ее можно применять, если для них существует неявное преобразование к этим типам. Тип результата операции равен "наибольшему" из типов операндов, но не менее int.
Если оба операнда целочисленные или типа decimal и результат операции слишком велик для представления с помощью заданного типа, генерируется исключение System.OverflowException.
Все возможные значения результата вычитания для вещественных операндов приведены в таб. 4.2.4-5. Символами х и y обозначены конечные положительные значения, символом z — результат операции вещественного вычитания. Если х и yравны, результат равен положительному нулю. Если результат слишком велик для представления с помощью заданного типа, он принимается равным значению "бесконечность" с тем же знаком, что х - y, если слишком мал, он принимается за 0 с тем же знаком, что х - y.
Таблица 4.2.4-5. Результаты вещественного вычитания | ||||||
- | y | +0 | -0 | +∞ | -∞ | NaN |
x | z | x | x | -∞ | +∞ | NaN |
+0 | -y | +0 | +0 | -∞ | +∞ | NaN |
-0 | -y | -0 | +0 | -∞ | +∞ | NaN |
+∞ | +∞ | +∞ | +∞ | NaN | +∞ | NaN |
-∞ | -∞ | -∞ | -∞ | -∞ | NaN | NaN |
NaN | NaN | NaN | NaN | NaN | NaN | NaN |
Операции присваивания (=, +=, -=, *= и т. д.) задают новое значение переменной.
Формат операции простого присваивания (=):
переменная = выражение
Механизм выполнения операции присваивания такой: вычисляется выражение и его результат заносится в память по адресу, который определяется именем переменной, находящейся слева от знака операции. То, что ранее хранилось в этой области памяти, теряется. Примеры операторов присваивания:
a = b + c / 2;
x = 1;
x = x + 0.5;
Для правого операнда операции присваивания должно существовать неявное преобразование к типу левого операнда. Например, выражение целого типа можно присвоить вещественной переменной, потому что целые числа являются подмножеством вещественных, и информация при таком присваивании не теряется.
вещественная_переменная = целое_выражение;
Результатом операции присваивания является значение, записанное в левый операнд. Тип результата совпадает с типом левого операнда.
В сложных операциях присваивания ( +=, *=, /= и т п.) при вычислении выражения, стоящего в правой части, используется значение из левой части. Например, при сложении с присваиванием ко второму операнду прибавляется первый, и результат записывается в первый операнд, то есть выражение a += b является более компактной записью выражения a = a + b.
Результатом операции сложного присваивания является значение, записанное в левый операнд.
Операции присваивания правоассоциативны, то есть выполняются справа налево, в отличие от большинства других операций (a = b = c означает a = (b = c)).
Операции отношения ( <, <=, >, >=, ==, !=).Операции отношения сравнивают значения левого и правого операндов. Результат операции логического типа: true – если значения совпадают, false – в противном случае. Рассмотрим операции на примере:
static void Main() { int i = 15, j = 15; Console.WriteLine(i<j); //меньше Console.WriteLine(i<=j); //меньшеилиравно Console.WriteLine(i>j); //больше Console.WriteLine(i>=j); //большеилиравно Console.WriteLine(i==j); //равно Console.WriteLine(i!=j); //неравно } | Результатработы : False True False True True False |
Рассмотренные операции приведены с учетом убывания приоритета. Если в одном выражении соседствуют операции одного приоритета, то операции присваивания и условная операции выполняются справа налево, а остальные наоборот. Если необходимо изменить порядок выполнения операций, то в выражении необходимо поставить круглые скобки.
4.2.4. Математические функции класса Math и
функции класса Random
Самая простая программа, которую можно себе представить состоит из ввода исходных данных, вычислений по каким-то формулам и вывода результата. В выражениях, из которых состоят формулы, часто используются математические функции, например, синус или возведение в степень. Они реализованы в классе Math, определенном в пространстве имен System. Описание методов и полей класса приведено в таб. 4.2.4-1.
Таблица 4.2.4-1. Основные поля и статические методы класса Math | |||
Имя | Описание | Результат | Пояснения |
Abs | Модуль | Перегружен | |x| записывается как Abs(x) |
Acos | Арккосинус | double | Acos(double x) |
Asin | Арксинус | double | Asin(double x) |
Atan | Арктангенс | double | Atan2(doublex, doubley) – угол, тангенс которого результат деления y на x |
BigMul | Произведение | long | BigMul(int x, int y) |
Ceiling | Округление до большего целого | double | Ceiling(double х) |
Cos | Косинус | double | Сos(double x) |
Cosh | Гиперболический косинус | double | Cosh(double x) |
DivRem | Деление и остаток | Перегружен | DivRem(x, y, rem) |
E | База натурального логарифма (число е) | double | 2,71828182845905 |
Exp | Экспонента | double | ex записывается как Exp(x) |
Floor | Округление до меньшего целого | double | Floor(double х) |
IEEERemainder | Остаток от деления | double | IEEERemainder(double x, double y) |
Log | Натуральный логарифм | double | logex записывается как Log(x) |
Log10 | Десятичный логарифм | double | log10x записывается как Log10(x) |
Max | Максимум из двух чисел | Перегружен | Max(x, y) |
Min | Минимум из двух чисел | Перегружен | Min(x, y) |
PI | Значение числа | double | 3,14159265358979 |
Pow | Возведение в степень | double | xy записывается как Pow(x, y) |
Round | Округление | Перегружен | Round(3.1) даст в результате 3 Round (3.8) даст в результате 4 |
Sign | Знак числа | int | Аргументы перегружены |
Sin | Синус | double | Sin(double x) |
Sinh | Гиперболический синус | double | Sinh(double x) |
Sqrt | Квадратный корень | double | √x записывается как Sqrt(x) |
Tan | Тангенс | double | Tan(double x) |
Tanh | Гиперболический тангенс | double | Tanh(double x) |
Нарис. 4.2.4-1приведен пример применения методов класса Math.
using System; namespace ConsoleApplication1 { class Class1 { static void Main() { Console.Write( "Введитех: " ); double x = double.Parse( Console.ReadLine() ); Console.Write( "Введите y: " ); double y = double.Parse( Console.ReadLine() ); Console.WriteLine( "Максимумизхи y : " + Math.Max(x, y) ); double z = Math.Pow(Math.Sin(x), 2)+Math.Pow(Math.Sin(y), 2); Console.WriteLine( "Сумма квадратов синусов х и y : " + z ); } } } |
Рис.4.2.4-1. Применение методов класса Math
Теперь намизвестно достаточно, чтобы писать последовательные (линейные программы), все операторы в которых выполняются последовательно, один за другим, например: ввод исходных данных, вычисления по формулам, вывод результатов.
Однако не рассмотрены правила описания процедур (методов класса).
Для написания программ более сложной структуры необходимо изучить управляющие операторы, которые, как следует из их названия, управляют потоками вычислений.
Умение генерировать случайные числа требуется во многих приложениях. Класс Random содержит все необходимые для этого средства. Класс Random имеет конструктор класса. Для того чтобы вызывать методы класса, нужно вначале создавать экземпляр класса. Этим Random отличается от класса Math, у которого все поля и методы статические, что позволяет обойтись без создания экземпляров класса Math.
Рассмотрим только оригинальные методы класса Random со статусом public, необходимые для генерирования последовательностей случайных чисел. Класс имеет защищенные методы, знание которых полезно при необходимости создания собственных потомков класса Random, но этим заниматься не будем.
Начнем рассмотрение с конструктора класса. Он перегружен и имеет две реализации. Одна из них позволяет генерировать неповторяющиеся при каждом запуске серии случайных чисел. Начальный элемент такой серии строится на основе текущей даты и времени, что гарантирует уникальность серии. Этот конструктор вызывается без параметров. Он описан как public Random(). Другой конструктор с параметром – public Random (int) обеспечивает важную возможность генерирования повторяющейся серии случайных чисел. Параметр конструктора используется для построения начального элемента серии, поэтому при задании одного и того же значения параметра серия будет повторяться.
Перегруженный метод public int Next() при каждом вызове возвращает положительное целое, равномерно распределенное в некотором диапазоне. Диапазон задается параметрами метода. Три реализации метода отличаются набором параметров:
· public int Next() – метод без параметров выдает целые положительные числа во всем положительном диапазоне типа int;
· public int Next(int max) – выдает целые положительные числа в диапазоне [0,max];
· public int Next(int min, int max) – выдает целые положительные числа в диапазоне [min,max].
Метод public double NextDouble() имеет одну реализацию. При каждом вызове этого метода выдается новое случайное число, равномерно распределенное в интервале [0-1).
Еще один полезный метод класса Random позволяет при одном обращении получать целую серию случайных чисел. Метод имеет параметр – массив, который и будет заполнен случайными числами. Метод описан как public void NextBytes(byte[] buffer). Так как параметр buffer представляет массив байтов, то, естественно, генерированные случайные числа находятся в диапазоне [0, 255].
Рассмотрим пример работы со случайными числами. Программный код этого метода представлен на рис.4.2.4-2 .
public void Rand() { const int initRnd = 77; Random realRnd = new Random(); Random repeatRnd = new Random(initRnd); // случайные числа в диапазоне [0,1) Console.WriteLine("случайные числа в диапазоне[0,1)"); for(int i =1; i <= 5; i++) { Console.WriteLine("Число " + i + "= " + realRnd.NextDouble() ); } // случайныечиславдиапазоне [min,max] int min = -100, max=-10; Console.WriteLine("случайныечиславдиапазоне [" + min + "," + max + "]"); for(int i =1; i <= 5; i++) { Console.WriteLine("Число " + i + "= " + realRnd.Next(min,max) ); } // случайный массив байтов byte[] bar = new byte[10]; repeatRnd.NextBytes(bar); Console.WriteLine("Массив случайных чисел в [0; 255]"); for(int i =0; i < 10; i++) { Console.WriteLine("Число " + i + "= " +bar[i]); } } |
Рис. 4.2.4-2
Прокомментируем текст программы. Вначале создаются два объекта класса Random. У этих объектов разные конструкторы. Объект с именем realRnd позволяет генерировать неповторяющиеся серии случайных чисел. Объект repeatRnd дает возможность повторить при необходимости серию. Метод NextDouble создает серию случайных чисел в диапазоне [0, 1). Вызываемый в цикле метод Next с двумя параметрами создает серию случайных положительных целых, равномерно распределенных в диапазоне [-100, -10]. Метод NextBytes объекта repeatRnd позволяет получить при одном вызове массив случайных чисел из диапазона [0;255]. Результаты вывода можно увидеть на рис. 4.2.4-3.
Рис. 4.2.4-3.
4.2.5. Преобразования типов и проверяемые преобразования
Необходимость в преобразовании типов возникает в выражениях, присваиваниях, замене формальных параметров метода фактическими параметрами.
Если при вычислении выражения операнды операции имеют разные типы, то возникает необходимость приведения операндов к одному типу. Такая необходимость возникает и тогда, когда операнды имеют один тип, но он несогласован с типом операции.
Преобразования ссылочных типов . При присваиваниях (замене аргументов) тип источника должен быть согласован с типом цели, что означает, что объект, связанный с источником, должен принадлежать классу, являющемуся потомком класса цели. В случае согласования типов ссылочная переменная цели связывается с объектом источника, и ее тип динамически изменяется, становясь типом источника. Это преобразование выполняется автоматически и неявно, не требуя от программиста никаких дополнительных указаний. Явное преобразование, заданное программистом, позволяет справиться с этим случаем. Ответственность за корректность явных преобразований лежит на программисте, так что может возникнуть ошибка на этапе выполнения, если связываемый объект реально не является объектом класса цели.
Преобразования типов в выражениях . Рассмотрим привычные для программистов преобразования внутри арифметического типа.
В C# такие преобразования делятся нанеявные и явные. К неявным преобразованиям относятся те, результат выполнения которых всегда успешен и не приводит к потере точности данных. Неявные преобразования выполняются автоматически. Для арифметических данных это означает, что для неявных преобразований диапазон типа назначения содержит в себе диапазон исходного типа. Например, преобразование из типа byte в тип int относится к неявным, поскольку диапазон типа byte является подмножеством диапазона int. Это преобразование всегда успешно и не может приводить к потере точности. Заметьте, преобразования из целочисленных типов к типам с плавающей точкой относятся к неявным. Хотя здесь и может происходить некоторое искажение значения, но точность представления значения сохраняется, например, при преобразовании из long в double порядок значения сохраняется.
К явным преобразованиям относятся разрешенные преобразования, успех выполнения которых не гарантируется или может приводить к потере точности. Такие потенциально опасные преобразования должны быть явно заданы программистом. Преобразование из типа int в тип byte относится к явным, поскольку оно не безопасно и может приводить к потере значащих цифр. Заметьте, не для всех типов существуют явные преобразования. В этом случае требуется другие механизмы преобразования типов, которые будут рассмотрены позже.
Преобразования внутри арифметического типа. Арифметический тип, как показано в табл. 4.2.2-1, распадается на 11 подтипов. На рис. 4.2.3-1 показана схема преобразований внутри арифметического типа:
Диаграмма, приведенная на рисунке, позволяет отвечать на ряд важных вопросов, связанных с существованием преобразований между типами. Если на диаграмме задан путь (стрелками) от типаА к типу В, то это означает существование неявного преобразования из типа А в тип В. Все остальные преобразования между подтипами арифметического типа существуют, но являются явными. Заметьте, что циклов на диаграмме нет, все стрелки односторонние, так что преобразование, обратное к неявному, всегда должно быть задано явным образом.
Иногда возникает ситуация, при которой для одного типа источника может одновременно существовать несколько типов назначений и необходимо осуществить выбор цели – типа назначения. Такие проблемы выбора возникают, например, при работе с перегруженными методами в классах.
Диаграмма, приведенная на рис. 4.2.3-1 и здесь помогает понять, как делается выбор. Пусть существует две или более реализации перегруженного метода, отличающиеся типом формального аргумента. Тогда при вызове этого метода с аргументом типа T может возникнуть проблема выбора, какую реализацию выбрать, поскольку для нескольких реализаций может быть допустимым преобразование аргумента типа T в тип, заданный формальным аргументом данной реализации метода. Правило выбора реализации при вызове метода таково – выбирается та реализация, для которой путь преобразований, заданный на диаграмме, короче. Если есть точное соответствие параметров по типу (путь длины 0), то, естественно, именно эта реализация и будет выбрана.
Рассмотрим пример. В класс Testing включена группа перегруженных методов OnLoad с одним и двумя аргументами (рис. 4.2.6-1).
// Группа перегруженных методов OLoad //с одним или двумя аргументами арифметического типа. //Если фактический аргумент один, то будет вызван один из методов, // наиболее близко подходящий по типу аргумента. // При вызове метода с двумя аргументами, возможен конфликт выбора // подходящего метода, приводящий к ошибке периода компиляции. void OLoad(float par) { Console.WriteLine("float value {0}", par); } // Перегруженный метод OLoad с одним параметром типа long void OLoad(long par) { Console.WriteLine("long value {0}", par); } // Перегруженный метод OLoad с одним параметром типа ulong void OLoad(ulong par) { Console.WriteLine("ulong value {0}", par); } // Перегруженный метод OLoad с одним параметром типа double void OLoad(double par) { Console.WriteLine("double value {0}", par); } // Перегруженный метод OLoad с двумя параметрами типа long и long void OLoad(long par1, long par2) { Console.WriteLine("long par1 {0}, long par2 {1}", par1, par2); } //Перегруженный метод OLoad с двумя параметрами типа double и double void OLoad(double par1, double par2) { Console.WriteLine("double par1{0},double par2 {1}", par1, par2); } // Перегруженный метод OLoad с двумя параметрами типа int и float void OLoad(int par1, float par2) { Console.WriteLine("int par1 {0}, float par2 {1}", par1, par2); } |
Рис. 4.2.6-1
Все эти методы устроены достаточно просто. Они сообщают информацию о типе и значении переданных аргументов. Вот тестирующая процедура, вызывающая метод OLoad с разным числом и типами аргументовприведены на рис.4.2.6-2.
// Вызов перегруженного метода OLoad. // В зависимости от типа и числа аргументов // вызывается один из методов группы. publicvoid OLoadTest() { OLoad(x); OLoad(ux); OLoad(y); OLoad(dy); //OLoad(x,ux); //conflict: ( int , float ) и ( long , long ) OLoad(x,(float)ux); OLoad(y,dy); OLoad(x,dy); } |
Рис. 4.2.6-2
Заметьте, один из вызовов закомментирован, так как он приводит к конфликту на этапе трансляции. Для устранения конфликта при вызове метода пришлось задать явное преобразование аргумента, что показано в строке, следующей за строкой-комментарием.
Прежде чем посмотреть на результаты работы тестирующей процедуры
(рис. 4.2.6-3), попробуйте понять, какой из перегруженных методов вызывается для каждого из вызовов. В случае каких-либо сомнений используйте схему, приведенную на 4.2.3-1.
Рис. 4.2.6-3. Вывод на печать результатов теста OLoadTest
При первом вызове метода тип источника – int, а тип аргумента у четырех возможных реализаций соответственно float, long, ulong, double. Явного соответствия нет, поэтому нужно искать самый короткий путь на схеме. Так как не существует неявного преобразование из типа int в тип ulong (на диаграмме нет пути), то остаются возможными три реализации. Но путь из int в long короче, чем остальные пути, поэтому будет выбрана long-реализация метода.
Следующий вызов демонстрирует еще одну возможную ситуацию. Для типа источника uint существуют две возможные реализации и пути преобразований для них имеют одинаковую длину. В этом случае выбирается та реализация, для которой на диаграмме путь показан сплошной, а не пунктирной стрелкой, потому будет выбрана реализация с параметром long.
Рассмотрим еще ситуацию, приводящую к конфликту. Первый аргумент в соответствии с правилами требует вызова одной реализации, а второй аргумент будет настаивать на вызове другой реализации. Возникнет коллизия, не разрешимая правилами C# и приводящая к ошибке периода компиляции. Коллизию требуется устранить, например, как это сделано в примере. Обратите внимание, обе реализации допустимы и будь только одна из них, ошибки бы не возникало.
Выражения над строками. На алфавите символов определен порядок, задаваемый Unicode кодировкой символов. Знать, как кодируется тот или иной символ не обязательно, но следует помнить, что кодировка буквенных символов таких алфавитов как кириллица, латиница и других языковых алфавитов, являющихся частью Unicode алфавита, является плотной, так что, например, код буквы «а» на единицу меньше кода буквы «б». Исключение составляет буква «Ё», выпадающая из плотной кодировки. Большие буквы (заглавные) в кодировке предшествуют малым буквам (строчным). Для цифр также используется плотная кодировка.
Поскольку каждому символу однозначно соответствует его код, то существует неявное, автоматически выполняемое преобразование в целочисленный тип (int и выше). Обратное преобразование также существует, но должно быть явным. Вот пример, показывающий как по символу получить его код, и как по коду получить символ, соответствующий коду.
char sym = 'Ё'; int code_Sym = sym; Console.WriteLine("sym = {0}, code = {1}", sym, code_Sym); code_Sym++; sym = (char)code_Sym; Console.WriteLine("sym = {0}, code = {1}", sym, code_Sym); |
Существование неявного преобразования между типом char и целочисленными типами позволяет рассматривать тип char как целочисленный. Как следствие, к операндам символьного типа применимы все операции, применимые к целочисленным типам – операции отношения, арифметические операции, логические операции над целыми числами. В реальных программах к таким операндам чаще всего применяются операции сравнения и операция вычитания, позволяющая определить «расстояние» между символами в кодировке.
Рассмотрим теперь строковый тип. В некоторых языках программирования используется тот факт, что порядок на символах алфавита порождает лексикографический порядок на словах, составленных их этих символов. Но в языке C# это не так, и здесь не существует неявного преобразования строки в целочисленный тип. Понять причину этого не трудно. Строки C# имеют переменную длину и могут быть сколь угодно длинными, так что не существует безопасного преобразования ни в один из целочисленных типов.
По этой причине над операндами строкового типа из множества операций, задаваемых знаками логических, арифметических и операций отношения, определены только три операции. Две операции позволяют сравнивать строк на эквивалентность (==, !=), Третья операция, задаваемая знаком операции «+», называется операцией конкатенации или сцепления строк и позволяет вторую строку присоединить к концу первой строки. Например,
string s1 = "Мир"; if (s1 == "Мир" | s1 == "мир") s1 += " Вам"; Console.WriteLine(s1); |
Явные преобразования строкового типа. Хотя неявных преобразований строкового типа в другие типы не существует, необходимость в преобразованиях существует. Когда пользователь вводит данные различных типов, то он задает эти данные как строки текста, поскольку текстовое представление наиболее естественно для человека. Поэтому при вводе практически всегда возникает задача преобразования данных, заданных текстом, в «настоящий» тип данных.
Классы библиотеки FCL предоставляют два способа явного выполнения таких преобразований: метод Parse и методы класса Convert.
Все скалярные типы (арифметический, логический, символьный) имеют статический метод Parse, аргументом которого является строка, а возвращаемым результатом объект соответствующего типа. Метод явно выполняет преобразование текстового представления в тот тип данных, который был целью вызова статического метода. Понятно, что строка, представляющая аргумент вызова, должна соответствовать представлению данных соответствующего типа. Если это требование не выполняется, то в ходе выполнения метода возникнет исключительная ситуация. Рассмотрим пример преобразования данных, выполняемых с использованием метода Parse.
static void InputVars() { string strInput; Console.WriteLine(INPUT_BYTE); strInput = Console.ReadLine(); byte b1; b1 = byte.Parse(strInput); Console.WriteLine(INPUT_INT); strInput = Console.ReadLine(); int n; n = int.Parse(strInput); Console.WriteLine(INPUT_FLOAT); strInput = Console.ReadLine(); float x; x = float.Parse(strInput); Console.WriteLine(INPUT_CHAR); strInput = Console.ReadLine(); char ch; ch = char.Parse(strInput); } |
Здесь приглашение к вводу задается соответствующей строковой константой. Поскольку вызов метода Parse способен приводить к исключительной ситуации, то корректно построенный ввод пользовательских данных предполагает помещение подобного вызова в охраняемый блок и построения соответствующего обработчика исключительной ситуации. Для краткости примера эта часть работы опущена.
Преобразования в строковый тип всегда определены, поскольку все типы являются потомками базового класса object и наследуют методToString().Для встроенных типов определена подходящая реализация этого метода. В частности, для всех подтипов арифметического типа метод ToString() возвращает строку, задающую соответствующее значение арифметического типа. Заметьте, метод ToString() следует вызывать явно. Его можно опускать при выполнении операции конкатенации. Если один из операндов операции «+» является строкой, то операция воспринимается как конкатенация строк и второй операнд неявно преобразуется к этому типу.
Для преобразований внутри арифметического типа можно использовать приведение типа. Для преобразований строкового типа в скалярный тип можно использовать метод Parse, а в обратную сторону – метод ToString.
Во всех ситуациях, когда требуется выполнить преобразование из одного базового встроенного типа в другой базовый тип, можно использовать методы класса Convert библиотеки FCL, встроенного в пространство имен System - универсального класса, статические методы которого специально спроектированы для выполнения преобразований.
Среди других методов класса Convert можно отметить общий статический метод ChangeType, позволяющий преобразование объекта к некоторому заданному типу.
Кроме методов, задающих преобразования типов, в классе Convert имеются и другие методы, например, задающие преобразования символовUnicode в однобайтную кодировку ASCII, преобразования, связанные с массивами, и другие методы. Подробности можно посмотреть в справочной системе.
Методы класса Convert поддерживают общий способ выполнения преобразований между типами. Класс Convert содержит 15 статических методов вида To<Type> (ToBoolean(),…ToUInt64()), где Type может принимать значения от Boolean до UInt64 для всех встроенных типов. Единственным исключением является тип object, – метода ToObject нет по понятным причинам, поскольку для всех типов существует неявное преобразование к типу object. Каждый из этих 15 методов перегружен и его аргумент x может принадлежать к любому из упомянутых типов. С учетом перегрузки с помощью методов этого класса можно осуществить любое из возможных преобразований одного типа в другой. Все методы осуществляют проверяемые преобразования и включают исключительную ситуацию всякий раз, когда преобразование осуществить невозможно или при выполнении преобразования происходит потеря точности. Например,
// Тестированиеметодовкласса Convert public void ConvertTest() { string s; byte b; int n; double x; bool flag; char sym; DateTime dt; sym = '7'; s = Convert.ToString(sym); x = Convert.ToDouble(s); n = Convert.ToInt32(x); b = Convert.ToByte(n); flag = Convert.ToBoolean(b); x = Convert.ToDouble(flag); s = Convert.ToString(flag); // sym = Convert.ToChar(flag); s = "300"; n = Convert.ToInt32(s); //b = Convert.ToByte(s); s ="14.09"; //flag = Convert.ToBoolean(s); //x = Convert.ToDouble(s); } |
Этот пример демонстрирует различные преобразования между типами. Все эти преобразования опасные, выполняются явно с использованием методов класса Convert. Вначале данные символьного типа преобразуются в строку. Затем эти данные преобразуются в вещественный тип, затем проводятся преобразования внутри арифметического типа с понижением типа от double до byte.
Опасные преобразования одного типа к другому могут успешно выполняться над некоторыми данными и приводить к ошибке с другими данными. В нашем примере закомментированы операторы, приводящие к ошибкам в период выполнения. Первая ошибка возникает при попытке преобразовать данные булевского типа к символьному типу, поскольку отсутствует преобразование, которое значение true преобразовывало бы в некоторый символ (например Т). Заметьте, в предыдущих операторах эти же данные успешно были приведены к строковому и арифметическому типу. Следующая ошибка возникает при попытке преобразовать строку со значением 300 к типу byte. Соответствующий метод распознает, что значение, записанное в строке, слишком велико для типа, представляющего цель преобразования. Еще одна ошибка возникает при попытке привести к булевскому типу строку, отличную от записи булевских констант true и false. Последняя ошибка в данном примере довольно часто встречается на практике. Она связана с тем, что ошибочно использована точка вместо запятой для отделения дробной части числа.
Какие выводы следует сделать? Опасные преобразования, выполняемые методами класса Convert, действительно опасны. По этой причине их всегда следует помещать в охраняемый блок и создавать блоки, обрабатывающие возможную исключительную ситуацию. Обработчик ситуации должен решать две важные задачи. Первая из них – информационная, он должен выдать достаточно подробную информацию с описанием возникшей ошибки. Вторая задача не менее важна. Обработчик должен попытаться исправить ситуацию, чтобы программа могла продолжить нормальное выполнение.
Проверяемые преобразования. Уже говорилось, что при выполнении явных преобразований могут возникать нежелательные явления, например, потеря точности. Причем вся ответственность за это ложится на программиста. Что можно предусмотреть для обнаружения ситуаций, когда такие явления все-таки возникают? В языке C# имеются необходимые для этого средства.
Язык C# позволяет создать проверяемый блок, в котором будет осуществляться проверка результата вычисления арифметических выражений. Если результат вычисления значения источника выходит за диапазон возможных значений целевой переменной, то возникнет исключение (говорят также, будет выброшено исключение) соответствующего типа. Если предусмотрена обработка исключения, то дальнейшее зависит от обработчика исключения. В лучшем случае программа сможет продолжить корректное выполнение. В худшем, – она остановится, и выдаст информацию об ошибке. Заметьте, не произойдет самого опасного – продолжения работы программы с неверными данными.
Синтаксически проверяемый блок предваряется ключевым словом checked. В теле такого блока арифметические преобразования проверяются на допустимость. Естественно, такая проверка требует дополнительных временных затрат. Если группа операторов в теле такого блока нам кажется безопасной, то их можно выделить в непроверяемый блок, используя ключевое слово unchecked. Замечу еще, что и в непроверяемом блоке при работе методов Convert все опасные преобразования проверяются и приводят к выбрасыванию исключений. Рассмотрим пример, демонстрирующий все описанные ситуации (рис. 4.2.6-8).
// Демонстрация проверяемых и непроверяемых преобразований // Опасные проверяемые преобразования приводят к исключениям, // Опасные непроверяемые преобразования // приводят к неверным результатам, что совсем плохо. publicvoid CheckUncheckTest() { x = -25^2; WhoIsWho ("x", x); b= 255; 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", x); Console.WriteLine("Исключений нет, но результаты не верны!"); } catch (Exception e) { Console.WriteLine("Этот текст не должен появляться"); Console.WriteLine(e); } //автоматическая проверка преобразований в Convert //исключения возникают, несмотря на unchecked try { b = Convert.ToByte(x); } catch (Exception e) { Console.WriteLine("Переполнение при преобразовании к byte!"); Console.WriteLine(e); } try { ux= Convert.ToUInt32(x); } catch (Exception e) { Console.WriteLine("Потерязнакаприпреобркint!"); Console.WriteLine(e); } } } } |
Рис. 4.2.6-8
Исключения и охраняемые блоки .В этом примере мы впервые встречаемся с охраняемымиtry-блоками. Исключениям и способам их обработки посвящена отдельная лекция, но не стоит откладывать надолго знакомство со столь важным механизмом. Как показывает практика программирования, любая вызываемая программа не гарантирует, что в процессе ее работы не возникнут какие-либо неполадки, в результате чего она не сможет выполнить свою часть контракта. Исключения являются нормальным способом уведомления об ошибках в работе программы. Возникновение ошибки в работе программы должно приводить к выбрасыванию исключения соответствующего типа, следствием чего является прерывание нормального хода выполнения программы и передача управления обработчику исключения – стандартному или предусмотренного самой программой.
В состав библиотеки FCL входит класс Exception, свойства и методы которого позволяют работать с исключениями как с объектами, получать нужную информацию, дополнять объект собственной информацией. У класса Exception большое число потомков, каждый из которых описывает определенный тип исключения. При проектировании собственных классов можно параллельно проектировать и классы, задающие собственный тип исключений, который может выбрасываться в случае ошибок при работе методов класса. Создаваемый класс исключений должен быть потомком класса Exception.
Если в некотором модуле предполагается возможность появления исключений, то разумно предусмотреть и их обработку. В этом случае в модуле создается охраняемыйtry-блок, предваряемый ключевым словом try. Вслед за этим блоком следует один или несколько блоков, обрабатывающих исключения – catch-блоков. Каждый catch-блок имеет формальный параметр класса Exception или одного из его потомков. Если в try-блоке возникает исключение типа T, то catch-блоки начинают конкурировать в борьбе за перехват исключения. Первый по порядку catch-блок, тип формального аргумента которого согласован с типом T – совпадает с ним или является его потомком – захватывает исключение и начинает выполняться. Поэтому порядок написания catch-блоков важен. Вначале должны идти специализированные обработчики. Универсальным обработчиком является catch-блок с формальным параметром родового класса Exception, согласованным с исключением любого типа T. Универсальный обработчик, если он есть, стоит последним, поскольку захватывает исключение любого типа.
Конечно, плохо, когда в процессе работы той или иной процедуры возникает исключение. Однако его появление еще не означает, что процедура не сможет выполнить свой контракт. Исключение может быть нужным образом обработано, после чего продолжится нормальный ход вычислений приложения. Гораздо хуже, когда возникают ошибки в работе процедуры, не приводящие к исключениям. Тогда работа продолжается с неверными данными без исправления ситуации и даже без уведомления о возникновении ошибки. Наш пример показывает, что вычисления в C# могут быть небезопасными и следует применять специальные средства языка, такие, как, например, checked-блоки, чтобы избежать появления подобных ситуаций.
Вернемся к обсуждению нашего примера. Здесь как в проверяемых, так и не проверяемых блоках находятся охраняемые блоки с соответствующими обработчиками исключительных ситуаций. Во всех случаях применяется универсальный обработчик, захватывающий любое исключение в случае его возникновения в try-блоке. Сами обработчики являются простыми уведомителями, они лишь сообщают об ошибочной ситуации, не пытаясь исправить ее.
Опасные вычисления в охраняемых проверяемых блоках . Такая ситуация возникает в первых двух try-блоках нашего примера. Эти блоки встроены в проверяемыйchecked-блок. В каждом из них используются опасные вычисления, приводящие к неверным результатам. Так при присваивании невинного выражения b+1 из-за переполнения переменная b получает значение 0, а не 256. Поскольку вычисление находится в проверяемом блоке, то ошибка вычислений обнаруживается и результатом является вызов исключения. Поскольку все это происходит в охраняемом блоке, то управление перехватывается и обрабатывается в соответствующем catch-блоке. Эту ситуацию следует отнести к нормальному, разумно построенному процессу вычислений.
Опасные вычисления в охраняемых непроверяемых блоках . Такую ситуацию демонстрирует третий try-блок нашего примера, встроенный в непроверяемый unchecked-блок. Здесь участвуют те же самые опасные вычисления. Но теперь их корректность не проверяется, они не вызывают исключений, как следствие, соответствующийcatch-блок не вызывается. Результаты вычислений при этом неверны, но никаких уведомлений об этом нет. Это самая плохая ситуация, которая может случиться при работе наших программ.
Заметьте, проверку переполнения в арифметических вычислениях можно включить не только с помощью создания checked-блоков, но и задав свойство checked проекта. По умолчанию это свойство выключено. Как правило, это свойство проекта всегда включается в процессе разработки и отладки проекта. В законченной версии проекта свойство отключается, поскольку полная проверка всех преобразований требует определенных накладных расходов, увеличивая время работы. В законченной версии остаются проверяемые блоки там, где такой контроль действительно необходим.
Область действия проверки или ее отключения можно распространить и на отдельное выражение. В этом случае спецификаторы checked и unchecked предшествуют выражению, заключенному в круглые скобки. Такое выражение называется проверяемым (непроверяемым) выражением, а checked и unchecked рассматриваются как операции, допустимые в выражениях.
Опасные преобразования и методы класса Convert. Явно выполняемые преобразования по определению относятся к опасным. Явные преобразования можно выполнять по-разному. Синтаксически наиболее просто выполнить приведение типа – кастинг, явно указав тип приведения, как это сделано в только что рассмотренном примере. Но если это приведение делается в непроверяемом блоке, последствия могут быть самыми печальными. Поэтому такой способ приведения типов следует применять с большой осторожностью. Надежнее преобразования типов выполнять более универсальным способом, используя стандартный встроенный класс Convert, специально спроектированный для этих целей.
В нашем примере четвертый и пятый try-блоки встроены в непроверяемый unchecked-блок. Но опасные преобразования реализуются методами класса Convert, которые сами проводят проверку и при необходимости выбрасывают исключения, что и происходит в нашем случае.
На рис. 4.5 показаны результаты работы процедуры CheckUncheckTest. Их анализ способствует лучшему пониманию рассмотренных нами ситуаций.
Рис. 4.2.6-9. Вывод на печать результатов теста CheckUncheckTest
Тема 4.3. Структура C#-программ,
процедуры– методы класса.
Дата: 2019-11-01, просмотров: 268.