ЛАБОРАТОРНАЯ РАБОТА 1
Классы. Наследование.
Обработка исключений в С#
Цель работы:
- познакомится с пользовательскими типами данных в языке С#: структура и перечисление;
- ознакомиться со структурой класса, его созданием и использованием, описанием членов класса: полей, свойств, инициализации объектов класса с помощью конструкторов;
- изучить механизм создания иерархий классов в С# и применение интерфейсов при наследовании;
- изучить механизм генерации и обработки исключений.
Задания к лабораторной работе
1. Проработать примеры 1-6, данные в теоретических сведениях. Создать на их основе программы. Получить результаты работы программ и уметь их объяснить. Внести в отчет с комментариями.
2. Для заданной структуры данных разработать абстрактный класс и класс наследник. В классе реализовать несколько конструкторов. Создать методы, работающие с полями класса. Часть из них должны быть виртуальными. Добавить методы-свойства и индексаторы.
3. Разработать интерфейсные классы, добавляющие некоторые методы в класс-потомок. Изучить причины возникновения коллизий имен при наследовании и способы устранения.
4. Разработать классы исключительных ситуаций и применить их для обработки, возникающих исключений.
5. Написать демонстрационную программу.
Описания данных пользовательских типов:
1. СЛУЖАЩИЙ: имя, возраст, рабочий стаж, список должностей.
2. ИЗДЕЛИЕ: название, шифр, количество, комплектация.
3. ПЕЧАТНОЕ ИЗДАНИЕ: название, ФИО автора, стоимость, оглавление.
4. ЭКЗАМЕН: ФИО студента, дата, оценка, перечень вопросов.
5. ТОВАР: название, артикул, стоимость, даты (изготовление, срок реализации)
6. ЦЕХ: название, начальник, кол-во рабочих, перечень номенклатуры выпускаемых изделий.
7. АВТОМОБИЛЬ: марка, мощность, стоимость, даты ремонта.
8. СТРАНА: название, форма правления, площадь, список областей.
9. ЖИВОТНОЕ: вид, класс, средний вес, места обитания.
10. КОРАБЛЬ: название, водоизмещение, тип, список категорий кают.
11. КАРТИНА: ФИО автора, название, жанр, список владельцев.
12. МУЗЕЙ: Название, адрес, ФИО директора, кол-во и названия залов.
13. КНИГА: Название, жанр, кол-во страниц, список авторов.
Краткие теоретические сведения
Для создания нового проекта C# заходим в меню Файл и далее выбираем Создать, Проект. В появившемся окне Создать проект слева выбираем Visual C#, а справа тип приложения – Консольное приложение. Дайте имя проекту - ConsoleHello, укажите, где будет храниться проект.
Главное окно редактора кода, в котором отображается программный код, хранимый в файле ConcoleHello.cs изображен на рис. 1.1. Ниже главного окна расположены окна вывода (Error List, Output), в которых выводится вся служебная информация.
В правой части окна находится Обозреватель решения, где показывается список файлов, содержащийся в "решении", которое, может состоять из нескольких "проектов”. Вкладки вверху главного окна позволяют легко перемещаться от одного открытого файла к другому.
Рисунок 1.1 - Консольный проект в Visual Studio NET
Добавим в проект код:
namespace ConsoleHello { // создаваемое пространство имен
class Program { // имя класса по умолчанию
static void Main(string[] args) { // главная функция, начало работы программы
// вывод строки на экран
Console.WriteLine("Введите Ваше имя");
string name;
name = Console.ReadLine(); // ввод строки с клавиатуры
if (name == "") Console.WriteLine("Здравствуй, мир!");
else Console.WriteLine("Здравствуй, " + name + "!");
}
} }
Пользовательские типы данных в С#
Перечисление - это частный случай класса. Перечисление задает конечное множество возможных значений, которые могут получать объекты класса перечисление. Синтаксис объявления этого класса: включает заголовок и тело класса, содержащее список возможных значений:
[атрибуты][модификаторы]enum имя_перечисления
[: базовый класс] {список_возможных_значений}
Особенности объявления перечислений:
· перечисления могут быть объявлены непосредственно в пространстве имен проекта или могут быть вложены в описание класса;
· константы разных перечислений могут совпадать. Имя константы всегда уточняется именем перечисления;
· константы могут задаваться словами русского языка;
· разрешается задавать базовый класс перечисления (любой целочисленный тип кроме long).
Например, для создания системы расчета заработной платы сотрудников можно использовать имена VP, Manager, Grunt и Contractor.
Значения по умолчанию: | Можно задать значения: |
еnиm ЕmрТуре { Manager, // = 0 Grunt, // = 1 Contractor, // = 2 VP // = 3 } | enum EmpType { Manager = 102, Grunt, // = 103 Contractor, // = 104 VP // = 105 } |
Структуры С# можно рассматривать как разновидность классов. Для структур можно определять конструкторы (только с параметрами), структуры могут реализовывать интерфейсы. Синтаксис объявления структуры:
[атрибуты][модификаторы]struct имя_структуры [:список_интерфейсов] {тело_структуры}
Программа 1. Использование перечислений и структур в программах на C#
enum EmpType: byte {
Manager = 10, Grunt = 1,
Contractor = 100, VP = 9
}
struct Employee {
public EmpType title; // Поле – перечисление
public string name;
public short deptID:
}
class StructTester {
public static int Main(string[] args) {
Employee fred; // Создание структурной переменной
fred.deptID = 40;
fred.name = "Fred";
fred.title = EmpType.Grunt;
return 0;
}}
Для структур можно определить конструкторы с параметрами:
struct Employee {
...
// Конструктор
public Employee (EmpType et, string n, short d) {
title = et; name = n; deptID = d;
}}
Теперь можно создавать новых сотрудников следующим образом:
class StructTester {
public static int Main(string[] args) {
Employee maгу =new Employee (EmpType.VP, "Mary", 10);
return 0;
}}
Классыявляются расширением структурного типа. Синтаксис описания класса:
[атрибуты][модификаторы]class имя_класса [:список_родителей] {тело_класса}
Модификаторы доступа к членам классов:
public - член доступен вне определения класса и иерархии производных классов.
protected - член невидим за пределами класса, к нему могут обращаться только производные классы.
private - член недоступен за пределами области видимости определяющего его класса.
internal - член видим только в пределах текущей единицы компиляции.
Обычно класс имеет модификатор доступа public, являющийся значением по умолчанию. Модификаторы private и protected используются для вложенных классов.
В теле класса могут быть объявлены: константы, поля, конструкторыидеструкторы, методы, события, делегаты, вложенные классы (структуры, интерфейсы, перечисления).
class Employee {
private string fullName;
private int empID;
private float currPay;
public Employee() {}
public Employee(string fullName, int empID, float currPay) {
this. fullName = fullName; this.empID = empID;
this. currPay = currPay;
}
// Метод для увеличения зарплаты сотрудника
public void GiveBonus(float amount)
{ currPay += amount; }
// Метод для вывода сведений о текущем состоянии объекта
public virtual void DisplayStats() {
Console. WriteLine("Name: {0}", fullName):
Console.WriteLine(“Pay: {0}", currPay):
Console.WriteLine(“ID: {0}", empID);
}
}
public static void Main() {
Employee e = new Employee(“Joe", 80, 30000);
e.GiveBonus(200):
Employee e2:
e2 = new Employee("Beth", 81, 50000);
e2.GiveBonus(1000);
e2.DisplayStats():
}
Методы-свойства. В языке C# принято поля объекта объявлять закрытыми, а нужную стратегию доступа организовывать через методы. Для эффективности этого процесса введены специальные методы-свойства. Они позволяют реализовать различные формы доступа к полям объекта.
Перечислим пять наиболее употребительных стратегий:
· чтение, запись (Read, Write);
· чтение, запись при первом обращении (Read, Write-once);
· только чтение (Read-only);
· только запись (Write-only);
· ни чтения, ни записи (Not Read, Not Write).
Рассмотрим класс Person, у которого пять полей: fam, status, salary, age, health, характеризующих соответственно фамилию, статус, зарплату, возраст и здоровье персоны. Для каждого из этих полей может быть своя стратегия доступа.
Программа 2. Свойства в класса на C#
public class Person {
string fam="", status="", health="";
int age=0, salary=0;
public string Fam { //стратегия: Read,Write-once
set {if (fam == "") fam = value;}
get {return(fam);}
}
public string Status { //стратегия: Read-only
get {return(status);}
}
public int Age { //стратегия: Read,Write
set {
age = value;
if(age < 7) status ="ребенок";
else if(age <17) status ="школьник";
else if (age < 22) status = "студент";
else status = "служащий";
}
get {return(age);}
}
public int Salary { //стратегия: Write-only
set {salary = value;}
}
}
public void TestPersonProps(){
Person pers1 = new Person();
pers1.Fam = "Петров"; pers1.Age = 21; pers1.Salary = 1000;
Console.WriteLine ("Фам={0}, возраст={1}, статус={2}", pers1.Fam, pers1.Age, pers1.Status);
pers1.Fam = "Иванов"; pers1.Age += 1;
Console.WriteLine ("Фам={0}, возраст={1}, статус={2}", pers1.Fam, pers1.Age, pers1.Status);
}
Индексатор. Обеспечивает доступ к закрытому полю, представляющему массив. Объекты класса индексируются по этому полю. У класса может быть только один индексатор со стандартным именем this.
Добавим в класс Person массив children, задающий детей персоны, сделаем это свойство закрытым, а доступ к нему обеспечит индексатор:
Программа 3. Индексаторы в класса на C#
…
const int Child_Max = 10; //максимальное число детей
Person[] children = new Person[Child_Max];
int count_children=0; //текущее число детей объекта
public Person this[int i] { //индексатор
get {
if (i>=0 && i< count_children) return(children[i]);
else return(children[0]);
}
set {
if (i==count_children && i< Child_Max) {
children[i] = value; count_children++;
}
}}
public void TestPersonChildren(){
Person pers1 = new Person(), pers2 = new Person();
pers1.Fam = "Петров"; pers1.Age = 42;
pers1.Salary = 10000; pers1[pers1.Count_children] = pers2;
pers2.Fam ="Петров"; pers2.Age = 21; pers2.Salary = 1000;
Console.WriteLine ("Фам={0}, возраст={1}, статус={2}", pers1.Fam, pers1.Age, pers1.Status);
Console.WriteLine ("Сын={0}, возраст={1}, статус={2}", pers1[0].Fam, pers1[0].Age, pers1[0].Status);
}
Статические поля и методы класса. У класса могут быть поля, связанные не с объектами, а с классом. Эти поляобъявляются как статические с модификатором static. Статические поля доступны всем методам класса. Независимо от объекта используется одно и то же статическое поле, позволяя использовать информацию, созданную другими объектами класса.
Аналогично полям, у класса могут быть и статические методы, объявленные с модификатором static. Такие методы обрабатывают общую для класса информацию, хранящуюся в его статических полях.
Наследование. Повторное использование кода - это одна из главных целей ООП. Класс- потомок наследует все возможности родительского класса - все поля и все методы, открытую и закрытую часть класса. Единственное, что не наследует потомок - это конструкторы родительского класса. Конструкторы потомок должен создавать сам. В C# разрешено только одиночное наследование, то есть у класса-потомка может быть только один родительский класс.
Рассмотрим класс Found, играющий роль родительского клаcса:
Программа 4. Родительский класс Found
public class Found{
protected string name;
protected int credit;
public Found() { }
public Found(string name, int sum) {
this.name = name; credit = sum;
}
public virtual void VirtMethod() { //виртуальный метод
Console.WriteLine ("Отец: " + this.ToString());
}
//переопределенный метод базового класса Object
public override string ToString() {
return(String.Format("поля: name = {0}, credit = {1}", name, credit));
}
public void NonVirtMethod() {
Console.WriteLine ("Мать: " + this.ToString());
}
public void Analysis() {
Console.WriteLine ("Простой анализ");
}
public void Work() {
VirtMethod();
NonVirtMethod();
Analysis();
}
}
Класс Found переопределил родительский метод класса Object ToString(), задав собственную реализацию возвращаемой методом строки. На переопределение родительского метода указывает модификатор override.
Создадим класс Derived - потомка класса Found. Потомок может добавить новые свойства - поля класса. Заметьте, потомок не может ни отменить, ни изменить модификаторы или типы полей, наследованных от родителя.
Класс Derived добавляет новое поле protected int debet.
Класс должен позаботиться о создании собственных конструкторов, поскольку, как правило, добавляет собственные поля, о которых родитель ничего не может знать. Если не задать конструкторы класса, то будет добавлен конструктор по умолчанию, инициализирующий все поля значениями по умолчанию. Но это редкая ситуация. Чаще всего, класс создает собственные конструкторы и, как правило, не один, задавая разные варианты инициализации полей.
public Derived(String name, int cred, int deb): base (name,cred)
{}
Вызов конструктора родителя происходит не в теле конструктора, а в заголовке, пока еще не создан объект класса.