Типы и классы. Переменные и объекты
Построение обработчиков событий
Перейдем к решению главной программистской задачи - созданию обработчиков события Click для кнопок "Ввод" и "Вывод". Наиболее интересным является обработчик события для кнопки "Ввод". Пользователь в тестовом окне задает имя скалярного типа. В ответ на его действия обработчик события должен создать объект этого типа. Возможное имя типа - это элемент конечного множества, перечисленного в таблице 2.1. Обработчик должен уметь выполнять работу, называемую в программировании "разбором случаев". Ему нужно понять, какой конкретный тип задал пользователь в текстовом окне, и, следовательно, переменную какого типа ему нужно создать. В C# для разбора случаев есть специальный оператор switch, каждая case -ветвь которого задает один из возможных вариантов. В этой ветви создается объект типа, заданного пользователем, и этому объекту присваивается значение, введенное пользователем в текстовое окно значений.
Поскольку пользователь вводит значение как текст (тип string ), возникает необходимость преобразования значения - от типа string к типу, заданному пользователем. При выполнении этого преобразования возможны ошибки по самым разным причинам. Например, пользователь мог задать значение, не принадлежащее множеству возможных значений данного типа. Другая причина - пользователь может не знать, как представляются значения данного типа, использовать точку, а не запятую для данных типа float или double. Возможны просто банальные ошибки - опечатки, неверный символ и так далее. Пользователь - человек, человеку свойственны ошибки, человек имеет право на ошибку. Задача обработчика события - выявить ошибку, если она возникла, сообщить о ней, дать возможность пользователю исправить ошибку и продолжить нормальную работу. В языке C# для этих целей предусмотрен специальный механизм охраняемых блоков, который и будет продемонстрирован в данном примере.
Давайте рассмотрим построенный код той части интерфейсного класса, в которой находятся обработчики событий, использующие упомянутые механизмы. Краткое описание этих механизмов будет дано в этой лекции. Более подробное их описание встретится позже в лекциях, специально посвященных этим механизмам. Начнем с описания полей и конструктора интерфейсного класса:
namespace SimpleVariables { public partial class FormTestTypes : Form { //fields string strType = ""; string strValue = ""; string strResult = ""; const string OK_MESSAGE = "Операция ввода прошла успешно!"; const string ERR_MESSAGE = "Значение, заданное при вводе, не принадлежит типу "; const string ERR_Type_MESSAGE = "Неверно задан скалярный тип!"; public FormTestTypes() { InitializeComponent(); textBoxType.Select(); }
Как правило, текстовым полям в интерфейсе класса ставятся в соответствие поля в интерфейсном классе, что облегчает обмен информацией между интерфейсными объектами и объектом, представляющим форму. Константы, являющиеся статическими полями класса, используются при выводе информационных сообщений. В конструктор класса, построенный по умолчанию, добавлен один оператор, позволяющий установить фокус ввода на текстовом окне, в котором пользователь должен задать тип переменной.
Приведу теперь код обработчика события Click командной кнопки buttonInput:
private void buttonInput_Click(object sender, EventArgs e) { strType = textBoxType.Text; strValue = textBoxValue.Text; //разбор вариантов switch (strType) { case "byte": { byte x; try { x = Convert.ToByte(strValue); textBoxResult.Text = OK_MESSAGE; strResult = x.ToString(); } catch (Exception) { textBoxResult.Text = ERR_MESSAGE + "byte!"; } break; } case "bool": { bool x; try { x = Convert.ToBoolean(strValue); textBoxResult.Text = OK_MESSAGE; strResult = x.ToString(); } catch (Exception) { textBoxResult.Text = ERR_MESSAGE + "bool!"; } break; } case "decimal": { decimal x; try { x = Convert.ToDecimal(strValue); textBoxResult.Text = OK_MESSAGE; strResult = x.ToString(); } catch (Exception) { textBoxResult.Text = ERR_MESSAGE + "decimal!"; } break; } case "object": { object x; try { x = strValue; textBoxResult.Text = OK_MESSAGE; strResult = x.ToString(); } catch (Exception) { textBoxResult.Text = ERR_MESSAGE + "object!"; } break; } default : { textBoxResult.Text = ERR_Type_MESSAGE; break; } }
Поскольку все case ветви оператора switch устроены одинаковым образом, в данном тексте большинство ветвей опущено. Если имя типа, заданное пользователем в текстовом окне textBoxType, совпадает с именем, указанным в соответствующей case -ветви, то именно эта ветвь и начинает выполняться. По ее завершении оператором break завершает работу и оператор разбора случаев switch. Если же пользователь задал "ошибочное" имя, то ни одна из case -ветвей не сработает, и тогда управление передается последней default -ветви этого оператора. Она устроена не так, как остальные ветви, - ее задача выдать сообщение о данной ошибке в текстовое окно, представляющее результаты выполнения операции.
Давайте на примере первой case ветви рассмотрим более подробно ее устройство. Еще до выполнения оператора switch обработчик события в поле класса strType и strValue читает информацию, записанную пользователем в соответствующие текстовые поля. Первая case -ветвь сравнивает значение поля strType с возможным вариантом - byte. Если значения совпадают, то пользователь задал тип "byte", поэтому в этой ветви и объявляется переменная этого типа. Рассмотрим три оператора этой ветви:
x = Convert.ToByte(strValue); textBoxResult.Text = OK_MESSAGE; strResult = x.ToString();
Первый из этих операторов присваивает переменной x значение strValue, введенное пользователем в соответствующее текстовое окно. В момент присваивания значение из строкового типа преобразуется к типу byte, заданному пользователем. Это преобразование типа выполняется методом ToByte класса Convert. Следующий оператор выдает сообщение об успехе операции в текстовое окно, информирующее пользователя о результате выполнения операции ввода. Последний оператор тройки формирует значение поля strResult, преобразуя значение типа byte в значение строкового типа.
Два последних оператора этой тройки безопасны, при их выполнении никогда не может произойти ошибки, обусловленной программными причинами (конечно, всегда возможен аппаратный сбой). Но вот первый оператор нормально завершит свою работу только тогда, когда пользователь задаст значение из достаточно узкого диапазона - допустимое значение для типа byte должно быть целым числом от 0 до 255. Во всех остальных случаях преобразование строки к типу byte приведет к ошибке, и возникнет так называемая "исключительная ситуация", когда программа не может продолжать нормально выполняться. Как бы хорошо не была написана программа, избежать возникновения в ней исключительных ситуаций не удается. В данном случае причиной может быть действие пользователя, задавшего некорректное значение. Избежать ситуации нельзя, но можно ее предвидеть и корректно обработать, позволяя продолжить нормальный ход выполнения программы.
В нашем примере это удается сделать за счет того, что оператор, при выполнении которого возможно возникновение исключительной ситуации, помещен в охраняемый try блок. Следом за охраняемым блоком располагается catch блок, которому будет передано управление в случае возникновения исключительной ситуации. Если же try -блок нормально завершит свою работу, то catch -блок выполняться не будет.
Задача catch -блока проста: выдать сообщение об ошибке выполнения операции, указать ее причину и продолжить выполнение проекта, дав пользователю возможность исправить свою ошибку. В данном случае совершенно ясна причина, по которой могла возникнуть исключительная ситуация, - неверно задано значение типа byte. Аппаратные сбои, весьма редкие в наше время, можно игнорировать.
Перейдем теперь к рассмотрению обработчика события Click командной кнопки buttonOutput. Он устроен совсем просто. После того как получила значение переменная x, объявленная в case-ветви, это значение предусмотрительно преобразовалось к строковому типу и сохранялось в поле strResult. Поэтому обработчику события достаточно передать значение этой переменной в текстовое окно. Если при вводе переменной была допущена ошибка, то результатом вывода является пустая строка. Вот код этого обработчика:
private void buttonOutput_Click(object sender, EventArgs e) { if (textBoxResult.Text == OK_MESSAGE) textBoxOutputValue.Text = strResult; else textBoxOutputValue.Text = ""; }