Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Объектно-ориентированное программирование. Язык С# представляет собой средство объектно-ориентированного и компонентно-ориентированного программирования

Язык С# представляет собой средство объектно-ориентированного и компонентно-ориентированного программирования. В языке С# все программы и данные представляют собой объекты, а все обрабатывающие их алгоритмы являются методами.

Описания объектов, с которыми мы имеем дело в жизни, можно представить себе

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

выполнять над данным объектом.

Возьмем, например, обычный телевизор. Ниже мы перечислили некоторые пара-

метры, которые могут характеризовать телевизор как объект:

• марка телевизора;

• название компании-изготовителя;

• габаритные размеры;

• вес;

• количество принимаемых каналов;

• возможность работы с пультом дистанционного управления;

• наличие выхода для подключения видеомагнитофона.

Если телевизор включен, то приобретают значение и другие параметры, например:

• номер принимаемого канала;

• громкость звука;

• стандарт видеосигнала (PAL, SEC AM, NTSC).

Можно сказать, что набор перечисленных выше характеристик представляет собой

данные объекта-телевизора. Такие данные полностью характеризуют объект сам по се-

бе, а также его текущее состояние.

Помимо данных существуют еще и методы работы с телевизором, т. е. действия,

выполняемые в процессе эксплуатации телевизора:

• включение электропитания;

• отключение электропитания;

• включение канала с заданным номером с панели телевизора;

• включение канала с заданным номером с пульта дистанционного управления;

• увеличение громкости;

• уменьшение громкости;

• временное отключение звука во время рекламных вставок;

• включение звука после временного отключения.

Возможность использования тех или иных методов работы с телевизором зависит

от его характеристик, а также от текущего состояния. Например, если телевизор

не может работать с пультом дистанционного управления, вы не сможете использо-

вать часть методов, например включение канала с помощью пульта. Если телевизор

выключен, его нельзя выключить еще раз, так как эта операция не имеет никакого

смысла. Если телевизор в состоянии принимать только 6 каналов, никаким образом

не удастся включить канал с номером 40 и т. д.

Таким образом, возможность и способы выполнения тех или иных операций с те-

левизором определяются его характеристиками и текущим состоянием, т. е. данными

о телевизоре.

Поставим перед собой задачу создания сильно упрощенной программной модели телевизора.

Данные

Характеристики и текущее состояние телевизора мы будем хранить в наборе перемен-

ных различных типов. Ниже мы перечислили эти переменные и привели их краткое

описание:

bool isPowerOn; // включен или выключен

byte maxChannel; // максимальный номер канала

byte currentChannel; // текущий номер канала

byte currentVolume; // текущая громкость звука

В переменной isPowerOn типа bool мы будем хранить состояние выключателя

электропитания телевизора. Если телевизор включен, в переменной isPowerOn бу-

дет находиться значение true, а если выключен — значение false.

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

в переменной maxChannel, а номер текущего канала, принимаемого в настоящий

момент времени, — в переменной currentChannel.

Кроме того, в переменной currentVolurae мы будем хранить уровень громкости,

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

равен 0 %, а если включен на максимальную громкость — 100 %.

Методы

Наша модель телевизора сможет выполнять несколько операций. Это операция инициали-

зации, включения и выключения телевизора, операции установки номера принимаемого

канала и уровня громкости, а также определение состояния телевизора (включен или вы-

ключен), определение текущего номера канала и текущего уровня громкости.

Для выполнения перечисленных операций в нашей программной модели преду-

смотрено несколько конструкций, называемых методами.

Метод объединяет в одном блоке программные строки, имеющие отношение к вы-

полнению той или иной операции. Метод может получать параметры и возвращать

значения.

Вот, например, как выглядит метод, включающий электропитание телевизора:

void SetPowerStateOn()

{

isPowerOn = true;

}

Ключевое слово void означает, что метод не возвращает никакого значения.

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

на практике это не всегда так), поэтому результат выполнения данной операции нас

не интересует.

После ключевого слова void идет название метода SetPowerStateOn. Мы выбрали его произвольно, однако с тем условием, чтобы это название отражало действие, выполняемое методом.

Далее в фигурных скобках располагаются операторы метода. Включение телевизо-

ра сводится к записи в переменную isPowerOn значения true. Пока мы отвлечемся

от способа определения этой переменной и будем считать, что методы имеют доступ

ко всем переменным, описывающим состояние нашего виртуального телевизора.

Чтобы выключить телевизор, мы определили метод SetPowerStateOf f:

void SetPowerStateOffО

{

isPowerOn = false;

}

Выключение телевизора сводится к записи в переменную isPowerOn значения false.

Чтобы определить, включен или выключен обычный телевизор, нам достаточно

взглянуть на его экран. Для определения состояния виртуального телевизора придется

создать несколько методов.

Метод GetPowerState позволяет получить текущее значение, хранящееся в пе-

ременной isPowerOn:

bool GetPowerState()

{

return isPowerOn;

}

Обратите внимание, что объявление метода начинается с ключевого слова bool.

Это означает, что метод должен вернуть логическое значение типа bool.

Для возвращения значения используется оператор return, упоминавшийся в кон-

це предыдущей главы. В данном случае этот оператор возвращает значение перемен-

ной isPowerOn. Если телевизор включен, метод GetPowerState возвратит значе-

ние true, в противном случае — значение false.

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

Мы определим методы, позволяющие установить заданный канал и определить

номер установленного канала.

Переключение виртуального телевизора на заданный канал можно сделать с помо-

щью метода SetChannel:

bool SetChannel(byte channel)

{

if(channel <= maxChannel && channel > 0)

{

currentChannel = channel;

return true;

}

else

return false;

}

В качестве параметра этому методу передается номер канала channel. Метод

проверяет номер канала и, если он больше нуля и не превышает максимально допус-

тимого, записывает новый номер канала в переменную currentChannel.

При успешном переключении канала метод SetChannel возвращает значение

true, а в случае ошибки — значение false. Это значение можно использовать для

выявления ошибочной попытки установить недопустимый номер канала.

Получить номер текущего канала можно с помощью метода GetChannel:

byte GetChannel()

{

return currentChannel;

}

Этот метод просто возвращает текущее содержимое переменной current-Channel.

Для управления громкостью мы предусмотрели метод SetVolume:

void SetVolume{byte volume)

{

if(volume > 0 && volume <= 100)

currentVolume = volume;

else

currentVolume = 0;

}

Он устанавливает новый уровень громкости, записывая значение volume в пере-

менную currentVolume. Перед этим, однако, наш метод проверяет, находится

ли новое значение громкости в диапазоне от 0 до 100 %. Если задан неправильный

уровень громкости, звук выключается.

Получить текущий уровень громкости можно с помощью метода GetVolume:

byte GetVolume()

{

return currentVolume;

)

Он просто возвращает текущее значение уровня громкости, хранящееся в перемен-

ной currentVolume.

В завершение мы определим метод, выполняющий инициализацию всех

переменных:

TelevisionSet()

{

// Устанавливаем исходное состояние телевизора

isPowerOn = false; // выключен

maxChannel = 40; // всего можно смотреть 4 0 каналов

currentChannel = 1; // при включении показывать канал 1

currentVolume = 10; // громкость при включении - 10%

}

Сразу после создания объекта (виртуального телевизора) этот метод выключает

электропитание телевизора, определяет максимальное количество каналов 40), уста-

навливает текущий номер канала, равный единице, а также задает начальный уровень

громкости — 10 % от максимального уровня.

Создание класса

Итак, мы создали данные — набор переменных, хранящих текущие параметры вирту-

ального телевизора, а также набор методов, позволяющий получать и изменять эти па-

раметры. Теперь нам нужно объединить данные и методы вместе, образовав описание

нашего объекта — виртуального телевизора.

В языке С# описание объекта представляется в виде нового типа данных и называется классом.

Вы создаете новый тип данных (класс), определяя для него набор переменных, на-

зываемых в этом случае полями класса, а также набор методов, предназначенных

для работы с этими полями.

В общем виде определение класса выглядит достаточно просто:

class <Имя класса>

{

[<Поле класса 1>]

[<Поле класса 2>]

[<Поле класса N>]

[<Конструктор>]

[<Метод класса 1>]

[<Метод класса 2>]

[<Метод класса М>]

}

После ключевого слова class идет название класса. Например, для нашего теле-

визора мы выберем название класса TelevisionSet:

class TelevisionSet

Объявление полей и методов класса находится внутри блока операторов, ограни-

ченного фигурными скобками. Вы можете размещать поля и методы внутри этого бло-

ка в произвольном порядке, однако предпочтительнее придерживаться какой-либо

стратегии.

Мы, например, размещаем внутри объявления класса вначале поля класса, а потом

методы, но можно поступить и наоборот. Вот пример объявления класса TelevisionSet

class TelevisionSet

{

bool isPowerOn; // включен или выключен

public byte maxChannel; // максимальный номер канала

byte currentChannel; // текущий номер канала

byte currentVolume; // текущая громкость звука

public TelevisionSet()

{

isPowerOn = false; // выключен

maxChannel =40; // всего можно смотреть 40 каналов

currentChannel =1; // при включении показывать канал 1

currentVolume =10; // громкость при включении - 10%

}

public void SetPowerStateOn{)

{

isPowerOn = true;

}

public void SetPowerStateOff()

{

isPowerOn = false;

}

}

D верхней части объявления класса находятся переменные isPowerOn, maxChannel, currentChannel и currentVolume, описанные ранее и отражающие текущее состояние телевизора. В контексте определения класса эти переменные обычно и называют полями класса.

Ниже полей объявлен метод, имя которого совпадает с именем нашего класса, —

TelevisionSet:

public TelevisionSet(byte numberOfChannels)

{

isPowerOn = false; // выключен

maxChannel = numberOfChannels; // макс, количество каналов

currentChannel= 1; // при включении показывать канал 1

currentVolume =10; // громкость при включении - 10%

}

Метод с таким именем называется конструктором класса. На конструктор класса

возлагается задача инициализации полей класса, поэтому мы поместили сюда не-

сколько операторов, определяющих начальное состояние телевизора.

Мы передаем конструктору один параметр numberOf Channels, определяющий

максимальное количество телевизионных каналов, которое способен показывать наш

телевизор.

По определению конструктор не может возвращать никакого значения. В отличие

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

слова vo id для обозначения этого обстоятельства.

Классификатор доступа public определяет один из видов доступа к полю класса

или методу. Здесь оно разрешает вызов конструктора из внешней программы.

Если в классе определен метод- конструктор, то он вызывается при создании объекта, описанного данным классом, с целью его инициализации.

Вслед за конструктором мы разместили в классе несколько описанных ранее методов, снабдив их ключевым словом public.

Поля и методы, а также другие объекты, объявленные в классе, часто называют

членами класса (class members).

Создание объектов класса

Теперь у нас есть класс TelevisionSet — описание телевизора, но нет самого теле-

визора. Пользуясь нашим классом, мы сможем создать необходимое нам количество

телевизоров.

Допустим, нам нужны два телевизора, большой, способный принимать до 40 кана-

лов, и маленький, рассчитанный на прием шести каналов.

Прежде всего для создания объектов-телевизоров необходимо объявить в програм-

ме две переменные типа TelevisionSet:

TelevisionSet tvSmall;

TelevisionSet tvLarge;

Как видите, объявление выглядит аналогично объявлению числовых или строчных

переменных, с той лишь разницей, что вместо одного из стандартных типов данных С#

мы использовали здесь определенный нами тип данных TelevisionSet.

На самом деле есть еще одно отличие.

В то время как стандартные типы данных можно использовать в программе сразу

после объявления, объекты других классов нужно создавать явным образом при по-

мощи ключевого слова new:

tvSmall = new TelevisionSet(6);

tvLarge = new TelevisionSet(40);

Здесь мы создали два объекта, вызвав их конструкторы. При первом вызове мы пе-

редали конструктору значение 6, а при втором — значение 40.

В результате всех этих действий программа записала в переменные tvSmall

и tvLarge так называемые ссылки на объекты. Переменная tvSmall ссылается

на объект-телевизор, способный принимать 6 каналов, а переменная tvLarge —

на 40-канальный телевизор.

Важно, что для создания этих разных телевизоров мы использовали один и тот же

класс, т. е. одно и то же описание объекта.

Отсюда наглядно видно различие между классом и объектом. Класс представляет

собой описание объекта, а объект — это реально существующая в памяти компьютера

сущность, с которой программа может выполнять какие-либо действия.

Вызов методов класса

После того как программа создала объекты, она может вызывать методы, определен-

ные в классе этих объектов.

Вызвать метод очень просто:

tvSmall.SetPowerStateOn();

tvSmall.SetChannel(5);

tvSmall.SetVolumeд(50);

tvLarge.SetPowerStateOn();

tvLarge.SetChannel(27);

tvLarge.SetVolume(30);

Здесь мы указали имя переменной, содержащей ссылку на объект, а затем через

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

следует указать в скобках.

Вызывая одни и те же методы для разных объектов, мы выполняем с этими объек-

тами одни и те же действия. Например, метод SetPowerStateOn включает наши те-

левизоры:

tvSmall.SetPowerStateOn();

tvLarge.SetPowerStateOn();

Чтобы переключить телевизоры на разные каналы, достаточно при вызове метода

SetChannel передать ему разные параметры:

tvSmall.SetChannel(6);

tvLarge.SetChannel(27);

Первый телевизор будет переключен на 6-й канал, а второй — на 27-й канал.

Аналогичным образом вы можете изменять громкость каждого телевизора, вызы-

вая для соответствующих объектов метод SetVolume:

tvSmall.SetVolume(50);

tvLarge.SetVolume(30);

Если метод возвращает значение, оно может быть присвоено переменной или ис-

пользовано в выражении. Вот, например, как можно узнать текущий номер канала

и текущий уровень громкости наших телевизоров:

byte chSmall = tvSmall.GetChannel();

byte volSmall= tvSmall.GetVolume();

byte chLarge = tvLarge. GetChannel();

byte volLarge = tvLarge.GetVolume();

После выполнения этих строк в переменных chSmall и volSmall будут записаны текущие значения номера канала и громкости первого телевизора, а в переменных chLarge и volLarge — второго. Заметим, что программа, создавшая объект, может обращаться только к таким методам, которые объявлены в описании класса со спецификатором доступа

public.

Обращение к полям класса

С помощью ссылки на объект программа может не только вызывать методы класса, но

и обращаться напрямую к его полям (если, конечно, они определены со спецификато-

ром доступа public, разрешающим такое обращение).

Вот, например, как можно узнать максимальное количество каналов, предусмот-

ренное в наших телевизорах:

byte maxChannelSmall = tvSmall.maxChannel;

byte maxChannelLarge = tvLarge.maxChannel;

При необходимости программа сможет изменить максимальное количество кана-

лов уже после создания телевизора:

tvSmall.maxChannel = 10;

tvLarge.maxChannel = 150;

Заметим, что максимальное количество каналов можно безболезненно увеличивать,

в то время как уменьшение следует выполнять осторожно. Например, если телевизор

переключен на 10-й канал, а вы устанавливаете максимальный номер канала, равный,

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

нал с недопустимым номером.

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

максимального количества каналов такой метод мог бы, например, установить номер

текущего канала равным единице.

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

поведения объекта. При этом программист, составляющий программу для работы

с объектом, может не отвлекаться на особенности внутреннего устройства объекта

и его реализации. Это позволит сконцентрировать внимание на других, более важных

задачах и снизить количество ошибок, допускаемых в процессе программирования.

Способность объекта скрывать детали своей внутренней реализации и внутренней

логики работы от вызывающих программ называется инкапсуляцией.

Существует два метода передачи параметров методу — по значению и по ссылке.

Когда значение параметра передается методу по значению, то метод получает это

значение и сохраняет его в своей локальной памяти (в локальном стеке). Хотя метод

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

это закончится безрезультатно. Если же параметр передается методу по ссылке, то метод может изменять значение соответствующей переменной. Для передачи параметров по ссылке в языке С# предусмотрено два ключевых слова— ref и out. Первое из них требует предварительной инициализации объекта, на который передается ссылка, а второе — не требует.

Программы С# работают с локальными переменными и полями классов различных типов. Это могут быть стандартные типы С# (числовые, строковые, логические и т. д.), а также типы данных, определенных при помощи классов. При составлении выражений, включающих в себя переменные и поля различных типов, выполняется операция преобразования типов (приведения типов). Эта операция может быть явной, когда программист сам указывает, к какому типу нужно привести данный тип, а также неявной. В последнем случае компилятор выполняет преобразование типов автоматически.

Если, например, у вас есть исходная переменная типа ulong, но вы точно знаете, что в ней

хранятся значения, не превосходящие 255, то можно выполнить следующее явное преобразование:

ulong ulongNuber = 11;

byte resultByte = (byte)ulongNuber;

Обратите внимание, что перед именем переменной ulongNuber мы указали в круглых скобках тип, к которому нужно привести исходный тип данных. Это так называемый оператор приведения типа. Оператор приведения типа заставляет компилятор выполнить заданное преобразование типов, даже если при этом исходное значение числа окажется искаженным.

Программист может также определить собственный способ преобразования типов.

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

программистом с помощью классов.

Обработка исключений.

Программы С# выполняются под управлением системы Microsoft.NET Framework, контролирующей появление ошибок. Если в программе не предусмотреть обработку ошибочной ситуации, то при появлении ошибки система Microsoft.NET Framework просто завершит ее работу с выдачей на экран соответствующего сообщения.

При этом ответственность за аварийное прекращение работы сбойной программы лежит на ОС. Если она не справится с этой задачей должным образом, могут возникнуть серьезные проблемы, вплоть до «зависания» компьютера и потери данных.

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

Система обработки ошибок, использованная в библиотеке классов Microsoft NET

Framework, работает исключительно с применением механизма исключений. С его помощью программист может организовать структурную обработку ошибок, как прогнозируемых, так и возникающих неожиданно.

Для того чтобы организовать в своей программе С# обработку ошибок с использова-

нием исключений, нужно применить блоки, созданные при помощи ключевых слов

try, catch и finally. Создание исключения (или, как еще говорят, передачу или

возбуждение исключения) выполняют с помощью ключевого слова throw.

В блок try заключается код, который может вызвать возникновение ошибок,

т. с. «ненадежный» код. Следом за блоком try может располагаться один или не-

сколько блоков catch, в которых находится код для обработки ошибок.

Рассмотрим пример обработки ошибки деления на нуль с помощью исключений

using System;

namespace DivideByZeroEx

{

class DivideByZeroExApp

{

static void Main(string[] args)

{

int i = 0;

try

{

int j = 5 / i;

}

catch(System.Exception ex)

{

Console.WriteLine(

">>> Ошибка в программе {0} [ {1} ] {2}”

ex.Source, ex.Message, ex.StackTrace);

}

Console.ReadLine();

Здесь мы заключили в блок try строку программы, при выполнении которой мо-

жет произойти ошибка деления на нуль:

try

{

int j =5 / i;

}

В блоке try может располагаться произвольное количество программных строк,

содержащих вызовы методов, обращения к интерфейсам, свойствам и индексаторам,

операторы цикла и т. п. При отсутствии ошибок все эти строки исполняются обычным

образом.

Если же в процессе выполнения какой-либо программной строки возникает ошиб-

ка, нормальная последовательность работы программы прерывается и управление пе-

редается ближайшему блоку catch, расположенному вслед за блоком try. Вот как

в нашей программе выглядит блок catch, предназначенный для обработки ошибки

деления на нуль:

catch(System.Exception ex)

{

Console.WriteLine(">>> Ошибка в программе {0} [{1)] {2}",

ex.Source, ex.Message, ex.StackTrace);

}

В круглых скобках после ключевого слова catch указывается тип (класс) обрабатываемого исключения. В данном случае мы указали базовый класс System.Exception, от которого «происходят» все остальные классы обработки исключений.

Сконструированный таким способом обработчик исключений способен перехватывать

исключения всех типов, возникающих при выполнении кода, ограниченного предше-

ствующим блоком try.

Вслед за обозначением класса обрабатываемого исключения мы указали имя пере-

менной ex. Когда блок catch получает управление, эта переменная содержит ссылку

на объект класса System. Exception. Свойства этого класса содержат информацию,

которая может оказаться полезной при обработке исключения:

• свойство System. Exception. Source содержит название программы, в которой

произошло исключение;

• в свойстве System. Exception. Message хранится текст сообщения об ошибке;

• свойство System. Exception. StackTrace содержит точную информацию

о том, в каком месте программы произошло исключение.

Вот как выглядит отформатированное сообщение об ошибке, отображаемое нашей

программой на консоли после попытки деления на нуль:

>>> Ошибка в программе DivideByZeroEx [Attempted to divide by zero.]

at DivideByZeroEx.DivideByZeroExApp.Main(String[] args) in

D: [student]Introductiondividebyzeroexdividebyzeroexapp.cs:line 11

Эта информация позволяет точно определить тип исключения, а также

место, в котором оно произошло.

 

<== предыдущая лекция | следующая лекция ==>
Лекция № 6. Знакомство с языком программирования С# | Лек ция № 7. Создание клиентских приложений Windows
Поделиться с друзьями:


Дата добавления: 2014-01-20; Просмотров: 577; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.216 сек.