
Laboratornyy_praktikum_Programmirovanie_na_C
.pdfвыполняющих завершенные действия, оформлены в виде методов. Скопируйте текст программы в новый проект, запустите на выполнение. Отдельно сохраните программу в Snake0.cs. Клавишами управления курсором (вверх, вниз, влево, вправо) перемещайте символ по полю. Нажатие пробела циклично переключает режим пера – перемещаемый символ может оставлять за собой след. Нажатие клавиши Esc прекращает работу программы. Ниже кода программы приведены пояснения к коду, а также задания, направленные на увеличение еѐ функциональности.
using System;
using System.Collections.Generic; using System.Text;
namespace Snake |
|
|
{ |
|
|
class Program |
|
|
{ |
|
|
const char |
Border = '#'; //Символ бордюра |
|
const char |
Sign = '@'; |
//Символ объекта |
static int |
ScrW, ScrH; |
//Размеры экрана |
static int |
X, Y; |
//Координаты объекта |
static bool Pen = false; //Отрисовка
static ConsoleColor ClrBg = ConsoleColor.Black; //Цвета static ConsoleColor ClrFg = ConsoleColor.Cyan;
static ConsoleColor ClrSg = ConsoleColor.Yellow;
static void ScreenInit() { //Подготовка экрана
ScrW = Console.WindowWidth; //Размер окна консоли ScrH = Console.WindowHeight;
string HBorder = new String(Border, ScrW); Console.BackgroundColor = ClrBg; Console.ForegroundColor = ClrFg; Console.Clear(); //Очистка Console.CursorVisible = false;
Console.SetCursorPosition(0, 0); //Отрисовка рамки Console.Write(HBorder);
Console.MoveBufferArea(0, 0, ScrW, 1, 0, ScrH - 1, Border, ClrFg, ClrBg);
for (int i = 1; i < ScrH - 1; i++)
{
Console.SetCursorPosition(0, i); Console.Write(Border); Console.SetCursorPosition(ScrW - 1, i); Console.Write(Border);
}
71
}
static void ScreenDone() { //Завершение программы
Console.BackgroundColor = ConsoleColor.Black; Console.ForegroundColor = ConsoleColor.Gray; Console.Clear(); //Восстановить цвета и очистить экран
Console.CursorVisible = true; //Показать курсор
}
static void ClearSg()
{
if (Pen) return; Console.SetCursorPosition(X, Y); Console.Write(' ');
}
static void WriteSg()
{
Console.SetCursorPosition(X, Y); Console.Write(Sign);
}
static void MoveSg(int XM, int YM)
{
ClearSg();
X += XM; Y += YM; WriteSg();
}
static void Go()
{
ConsoleKeyInfo Key;
//Начальные координаты - центр экрана
X = ScrW / 2; Y = ScrH / 2;
//Установить цвет, позицию и вывести
Console.ForegroundColor = ClrSg; WriteSg();
//В цикле считать клавишу и for (; ; )
{
Key = Console.ReadKey(true); switch (Key.Key)
{
case ConsoleKey.Escape:
return; //ESC завершает программу case ConsoleKey.Spacebar:
Pen = !Pen; break;
case ConsoleKey.LeftArrow:
72
if (X > 1) MoveSg(-1, 0); else Console.Beep(); break;
case ConsoleKey.RightArrow:
if (X < ScrW - 2) MoveSg(1, 0); else Console.Beep();
break;
case ConsoleKey.UpArrow:
if (Y > 1) MoveSg(0, -1); else Console.Beep(); break;
case ConsoleKey.DownArrow:
if (Y < ScrH - 2) MoveSg(0, 1); else Console.Beep();
break; default:
Console.Beep(); break;
}
}
}
static void Main(string[] args)
{
ScreenInit();
Go();
ScreenDone();
}
}
}
Вся программа помещена в класс Program. В начале класса задан перечень констант и статических переменных, являющимися для методов класса глобальными. Т.к. ни поля (переменные класса), ни методы класса не используются вне класса, у них нет для упрощения записи ключевого слова public. Статические поля и методы инициализируются один раз при старте программы и присутствуют в классе в единственном экземпляре. Т.е. при создании нового экземпляра класса в экземпляре создаются новые только динамические члены, статические остаются общими для всех экземпляров объектов. Статические члены в примере программы используются для упрощения записи и понимания работы подпрограмм. Более подробно классы и объектно-ориентированное программирование будет рассмотрено на следующих занятиях.
Константа Border содержит символ бордюра, Sign – перемещаемого символа. Переменные (поля класса) ScrW и ScrH после заполнения хранят размеры экрана. В программе нет проверки на изменение размера консоли во время работы программы, поэтому значения считываются единожды при старте. Переменные X и Y содержат текущие координаты перемещаемого символа. При старте значения координат
73
высчитываются для центра консоли. Переменные ClrBg, CrlFg, ClrSg соответственно содержат значения цвета для фона, рамки и символа.
Все методы программы имеют тип void, т.е. не возвращают значений.
Метод ScreenInit() подготавливает поле консоли к интерактивному режиму: считываются и заполняются значения ширины и высоты консоли; создаѐтся строка горизонтальной линии бордюра; задаѐтся цвет; очищается экран; гасится курсор; т.к. координаты нумеруются с нуля, начиная с поля 0,0, выводится строка верхней горизонтальной линии бордюра; далее используется трюк перемещения области экрана в видеопамяти методом MoveBufferArea() с заполнением перемещаемой области тем же символом и тем же цветом, т.к. после вывода оператором Write символа в нижний правый угол консоли курсор выйдет за границы и экран будет прокручен на строку вверх, перемещение содержимого видеопамяти к прокрутке не приводит; далее в цикле построчно выводятся символы вертикальных линий бордюра.
Метод ScreenDone() нужен для очистки консоли, когда программа запускается как самостоятельное приложение консоли. В нѐм устанавливаются цвета серый на чѐтном, производится очистка консоли и включается отображение курсора.
Метод CrearSg() используется для удаления символа на консоли, выводом пробела в позицию символа. В начале метода есть проверка, включен ли режим пера. В режиме пера удаления символа не производится, а происходит выход из метода
(return).
Метод WriteSg() в текущую позицию выводит символ.
Метод MoveSg() удаляет символ вызовом метода ClearSg(), затем изменяет текущие координаты на значения, которые передаются в качестве параметров. Затем уже в новой текущей позиции символ выводится методом WriteSg().
Метод Go() является основным в программе. В нѐм задаѐтся переменная, хранящая код нажатой клавиши; задаѐтся текущая позиция символа в центре консоли; задаѐтся цвет вывода символа и символ выводится на консоль. Далее оператором for(;;) задаѐтся вечный цикл, внутри которого производится считывание кода нажатой клавиши и его обработка в операторе выбора. В операторе выбора производится проверка на следующие клавиши: Esc выходит из метода, пробел инвертирует (изменяет на обратный) флаг режима пера, в блоках клавиш вверх, вниз, влево и вправо производится проверка на достижение границы и перемещение символа по соответствующей координате. Нажатие любой другой символьной клавиши генерирует звуковой сигнал.
Метод Main(), который автоматически выполняется при старте программы, содержит вызовы трѐх методов – ScreenInit() для подготовки консоли, Go() для интерактивного взаимодействия и ScreenDone() для очистки консоли после выхода из метода Go().
6.2.1.Задача. Добавление реакции на клавиши
Вприведенной программе Snake произведите изменения. Необходимо, чтобы программа дополнительно реагировала на клавиши дополнительной цифровой клавиатуры в режиме управления курсором. В примере задействованы клавиши вверх, вниз, влево и вправо. Добавьте реакцию на клавиши Home, End, Page Up, Page Down для перемещения символа по диагонали в соответствующих расположению клавиш направлениях. При нажатии точки после ввода имени класса ConsoleKey
74
открывается список клавиш. Описание этого класса нужно просмотреть в справочной системе, нажав F1, когда курсор ввода находится в области слова. Обратите внимание, что недостаточно добавить приращение координат, необходимо скорректировать условие, иначе символ будет попадать на область бордюра. Например, нажатие клавиши Home сочетает действия влево и вверх, значит, должны объединяться условия этих клавиш логическим укороченным оператором И. Используя возможность проваливания из пустого условия к существующим клавишам можно добавить буквенные клавиши, применяемые в играх (QWE ASD ZXC). Cохраните программу в Snake1.cs.
6.2.2.Задача. Добавление счётчика игры
Впрограмме Snake выполните дополнительные изменения. В глобальных целых переменных добавьте X1, Y1 – координаты, N – значение числа, которое должно появляться в окне, Count – счѐтчик игры, инициализировать в ноль. При инициализации метода Go() вызовите статический метод Num(), в котором сгенерируйте и присвойте с помощью случайных чисел (см. в справке Random.Next(Int32, Int32)) координаты числа внутри бордюра и значение числа от 1 до 9 включительно, выведите значение Count вверху слева в строку бордюра. При перемещении символа проверяйте координаты, и в случае совпадения X,Y и X1,Y1 увеличьте значение счѐта игры Count на N и вызовите метод Num() для генерации следующего числа и его координат (с выводом счѐта). Cохраните программу в
Snake2.cs.
Более подробно генераторы случайных чисел будут рассматриваться позже.
6.2.3.Задача. Добавление автоматического движения*
Впрограмме Snake выполните дополнительные изменения. Введите новые глобальные целые переменные DX и DY, инициализированные в ноль и хранящие направление перемещения по каждой координате (–1, 0 или 1). Также добавьте переменную Tm, хранящую время (тип DateTime), инициализированную в методе Go() до цикла значением текущего времени и переменную Delay (типа TimeSpan, описывающего интервалы времени):
DateTime Tm = DateTime.Now; var Delay = new TimeSpan();
Т.к. метод ReadKey() ожидает нажатия клавиши, и во время ожидания не могут выполняться другие операторы, поместите блок считывания и проверки кода клавиши в оператор if (Console.KeyAvailable) {}, который выполнит чтение только после того, как клавиша уже нажата и код помещен в буфер клавиатуры. Из блока проверки клавиатуры уберите вызов метода MoveSg(), оставив только изменение переменных DX и DY, определяющих направление перемещения.
После полученного блока проверки клавиатуры необходимо вставить блок проверки истечения времени:
Delay = DateTime.Now - Tm;
if (Delay.TotalMilliseconds > 500.0)
{
Tm = DateTime.Now;
75
if ((X + DX > 1) && (X + DX < ScrW - 2) && (Y + DY > 1) && (Y + DY < ScrH - 2))
{
MoveSg(DX, DY);
Tm = DateTime.Now;
}
else
{
Console.Beep();
Key = Console.ReadKey(true); return;
}
}
Вэтом блоке в переменную Delay заносится разница во времени между предыдущим перемещением (или стартом) и текущим временем. Если интервал превышает 0,5 секунды (500 мс), то происходит дополнительная проверка координаты следующего положения. Если координаты находятся внутри поля, ограниченного бордюром, то производится перемещение символа в направлении, заданном последним нажатием клавиш управления курсором, иначе (если координаты совпали с координатами бордюра) выдаѐтся звуковой сигнал, игра останавливается, ожидается нажатие любой клавиши и программа завершается.
Cохраните программу в Snake3.cs.
6.2.4.Задача. Добавление хвоста**
Впрограмме Snake выполните дополнительные изменения. Удалите режим пера и реакцию на нажатие клавиши пробел. Продумайте и реализуйте игру так, чтобы при движении длина «змейки» соответствовала количеству очков, полученных при «съедании» цифры. Реализовать вывод одновременно нескольких чисел (использовать массивы), проверяя генерируемые координаты на принадлежность пустому полю. По мере «съедания» цифр генерировать новые. Игру останавливать не только тогда, когда змейка «укусила» бордюр, но и собственное тело.
Счѐт выводите цветом, отличным от цвета бордюра. Cохраните программу в Snake4.cs.
Работа с объектами и массивами будет рассматриваться позже.
6.2.5.Задача. Наведение глянца
Впрограмме Snake выполните дополнительные изменения. Цифры выводите разными цветами. Задайте для отображения тела змейки символ и цвет, отличные от символа головы. Введите глобальную переменную задержки DT двойной точности, инициализированную в 500D, замените ей константу 500.0 в проверки задержки. По мере набора каждых 25 очков уменьшайте значение DT на единицу, тем самым ускоряя темп игры.
Cохраните программу в Snake5.cs.
76
Покажите результаты работы преподавателю, закройте Visual Studio, а затем
уд л т с со д нны м о р мя р боты ф йлы п пк в папке
D:\Program\VS.
Вопросы к под ото к :
1.Что такое методы?
2.На что влияют модификаторы методов?
3.Как задать тип возвращаемых данных?
4.Как указать, что метод не возвращает никаких значений?
5.Как в методы передать параметры?
6.Как из метода получить результат?
77
ЛАБОРАТОРНАЯ РАБОТА 7. КЛАССЫ И ОБЪЕКТЫ
Цель работы: Разобраться в основах объектно-ориентированного программирования, выполнить примеры. Программы работы записать (вклеить распечатку) с пояснениями в лабораторный журнал.
7.1. ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ
Объектно-ориентированное направление программирования (ООП) появилось в конце семидесятых годов XX века из-за необходимости реализации больших проектов, которым уже не удовлетворяли возможности структурного программирования.
Все программы на C# являются объектно-ориентированными.
ООП основано на следующих свойствах: абстракция, инкапсуляции, полиморфизме и наследовании.
7.1.1. Классы, объекты
Кл сс – разновидность абстрактного т п данных в ООП, характеризуемый способом своего построения. Класс представляет собой шаблон для создания объектов. Объ кты являются экземплярами классов. Класс – логическая абстракция, описание, по которому строятся объекты. Объекты – реальные структуры реализации классов, занимающие после создания оперативную память. Классы можно сравнить с чертежами, по которым можно создать реальные объекты. Для создания класса выделяют логически различимое направление рассматриваемой области (абстракция). Классы, как и получаемые по их описаниям объекты состоят из объединения членов (инкапсуляция) – данных-полей и кода-методов. Методы объектов близкой спецификации могут иметь одинаковые имена и назначение, но выполнять действия, характерные для конкретного объекта (полиморфизм). При необходимости получения новых возможностей можно добавить дочернему классу недостающие члены, позаимствовав уже существующие у родительского класса
(наследование).
7.1.2. Абстракция
Абстр кц я [1] – это придание объекту характеристик, которые отличают его от всех других объектов, четко определяя его концептуальные границы. Основная идея состоит в том, чтобы отделить способ использования составных объектов данных от деталей их реализации в виде более простых объектов, подобно тому, как функциональная абстракция разделяет способ использования функции и деталей еѐ реализации в терминах более примитивных функций. Таким образом, данные обрабатываются функцией высокого уровня с помощью вызова функций низкого уровня.
Такой подход является основой объектно-ориентированного программирования. Это позволяет работать с объектами, не вдаваясь в особенности их реализации. В каждом конкретном случае применяется тот или иной подход: инкапсуляция,
78
полиморфизм или наследование. Например, при необходимости обратиться к скрытым данным объекта, следует воспользоваться инкапсуляцией, создав, так называемую, функцию доступа или свойство.
Правильно сконструированный кл сс долж н опр д лять только одну ло ч скую сущность. Например, класс описания технологического оборудования не содержит описание сотрудника и наоборот. В правильно сконструированном классе должна быть сгруппирована логически связанная информация, иначе структурированность кода будет нарушена. Абстракция выделяет рассматриваемый объект, определяет набор данных, описывающих его и действия, которые можно с ними производить.
Класс может иметь модификатор abstract, который указывает, что описываемый класс является абстрактным, т.е. не может использоваться для создания экземпляров, а только для создания дочерних классов.
7.1.3. Инкапсуляция
Инк псуляц я – свойство языка программирования, позволяющее объединить и защитить данные и код в объекте и скрыть реализацию объекта от пользователя (прикладного программиста). При этом пользователю предоставляется только спецификация (интерфейс) объекта.
Пользователь может взаимодействовать с объектом только через этот интерфейс. Реализуется с помощью ключевого слова: public.
Пользователь не может использовать закрытые данные и методы. Реализуется с помощью ключевых слов: private, protected, internal.
Модификаторы доступа, указывающие уровень доступности к членам класса:
Объявленная доступность |
Описание |
public |
Неограниченный доступ |
protected |
Доступ ограничен содержащим классом или типами, |
|
которые являются производными от содержащего |
|
класса |
internal |
Доступ ограничен текущей сборкой |
protectedinternal |
Доступ ограничен текущей сборкой или типами, |
|
которые являются производными от содержащего |
|
класса |
private |
Доступ ограничен содержащим типом |
Инкапсуляция объединяет в классе описывающие этот объект поля данных и код, обрабатывающий эти данные. Для унификации использования классов родственных направлений принято работать с данными объектов через методы. Специфические методы, называемые свойствами объектов, позволяют не только изменять поля объекта, но и выполнять сопутствующие действия. Например, присвоение значения свойству графического объекта не только меняет соответствующие данные (поля объекта), но и меняет графическое изображение, такое как форма, размер, положение, цвет или контур. Единообразие именования свойств, методов и полей объектов схожих направлений, позволяющих использовать логически идентичные идентификаторы членов для разных реализаций, называется
полиморфизмом.
79
7.1.4. Полиморфизм
Пол морф м (греч., множество форм) – возможность объектов с одинаковой спецификацией иметь различную реализацию.
Полиморфизм позволяет писать более абстрактные программы и повысить коэффициент повторного использования кода. Общие свойства объектов объединяются в систему, которую могут называть по-разному – интерфейс, класс. Общность имеет внешнее и внутреннее выражение:
внешняя общность проявляется как одинаковый набор методов с одинаковыми именами и сигнатурами (именем методов и типами аргументов и их количеством);
внутренняя общность – одинаковая функциональность методов. Еѐ можно описать интуитивно или выразить в виде строгих законов, правил, которым должны подчиняться методы. Возможность приписывать разную функциональность одному методу (функции, операции) называется перегрузкой метода.
Примером полиморфизма может быть наличие у классов, описывающих графические объекты методов Show(), Hide(), Move() для соответственно отображения, скрытия и перемещения объекта. Объекты точки, прямоугольника и эллипса отрисовываются по-разному, но пользователю понятна запись Ellipse1.Show(), Point1.Show() и т.д. (показать эллипс, показать точку).
7.1.5. Наследование
Н сл до н – механизм ООП, позволяющий описать новый класс на основе уже существующего (родительского), при этом свойства и функциональность родительского класса заимствуются новым классом.
В дочернем классе можно переписать функциональность метода, уже существующего в родительском классе, скрывая наследованные члены. Для этого строку описания перекрывающего метода необходимо начать с модификатора new (см. справку). Для расширения реализации наследуемого члена вместо скрытия используется модификатор override (см. справку). При наследовании нового класса от родительского, имя родительского класса указывается через двоеточие.
7.1.6. Оператор new
Перед обращением к объекту, его нужно создать, т.к. класс – всего лишь абстракция, описание. Экземпляр объекта создаѐтся оператором new. Переменная экземпляра объекта является ссылочной, т.е. хранит ссылку на область памяти, в которой создан объект, а не сам объект. Присваивание переменной, ссылающейся на объект другой переменной такого же типа, заменит в ней ссылку на копию первой, что может быть причиной утери ссылки на второй объект.
Поля и методы, имеющие модификатор static, создаются автоматически при старте программы и описывают статический член, принадлежащий всему типу, а не конкретному объекту. Модификатор static можно использовать с классами, полями, методами, свойствами, операторами, событиями и конструкторами, но нельзя – с индексаторами, деструкторами или типами, отличными от классов. К статически методам относится метод Main(), который может быть только в одном классе, и с которого начинается программа.
80