Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ C#.docx
Скачиваний:
16
Добавлен:
20.11.2019
Размер:
1.85 Mб
Скачать

3.7. Работа со структурами

Класс является ссылочным типом: доступ к его объектам осуществляется с помощью ссылок. Доступ к объектам класса с помощью ссылок вызывает дополнительные накладные расходы при каждом доступе. При работе с маленькими объектами дополнительные расходы могут иметь существенное значение. С целью решения этой проблемы в C# введены структуры. Структура подобна классу, но она имеет тип значение, но не ссылка. Внешне объявление структуры похоже на объявление класса. Структуры могут иметь в своем составе данные, методы, индексаторы, свойства. Конструкторы тоже разрешены, но они обязательно должны иметь параметры; деструкторы – нет. Для создания экземпляра структуры можно вызвать конструктор через new, но можно и не вызывать. В таком случае экземпляр структуры создается, но записанные в конструкторе операции не будут выполнены. Структуры не могут участвовать в процессе наследования, ни в качестве предков, ни в качестве потомков. Исключение: в качестве предка структуры можно указать интерфейс (об интерфейсах поговорим позже).

namespace StructFun

{

struct dan1

{

public string s1; // атрибут public обязателен

public int k;

}

class Program

{

static dan1[] inpt()

{ // ввод массива структур

dan1[] temp;

int n;

Console.Write("Elements ? ");

n = Convert.ToInt32(Console.ReadLine());

temp = new dan1[n];

for (int i = 0; i < n; i++)

{

Console.Write("Elem " + i + " Num ");

temp[i].k = Convert.ToInt32(Console.ReadLine());

Console.Write("Elem " + i + " Name ");

temp[i].s1 = Console.ReadLine();

}

return temp;

}

static double proc1(dan1 []x)

{ // обработка массива структур

int s = 0;

for (int i = 0; i < x.Length; i++)

s += x[i].k;

return (double)s / x.Length;

}

static void Main(string[] args)

{

dan1[] id; // массив исходных данных

double aver;

id = inpt(); // ввод исходных данных

aver = proc1(id); // обработка массива структур

Console.WriteLine("Average=" + aver);

Console.ReadLine();

} } }

3.8. Наследование

В C# допускается простое наследование: каждый класс может иметь только одного предка. Используя наследование, можно создать базовый класс, который определяет характеристики, присущие множеству связанных объектов. Этот класс затем может быть унаследован другими классами с до­бавлением в каждый из них своих особенностей. Равнозначные термины: ба­зовый класс – класс наследник; родительский класс – дочерний класс; класс предок – класс наследник.

Создадим в качестве примера базовый класс для обработки массива, вклю­чающий определение массива, его ввод и вывод. К элементам базового класса с атрибутом доступа private нет доступа из классов – наследников, они, таким образом, не наследуются. Поэтому рекомендуют (если нет на этот счет особых соображений) дать элементам базового класса атрибут доступа protected.

class arr

{

protected int[] k; //атрибут доступа protected

//необходим для обеспечения доступа из классов - наследников

public arr()

{ // конструктор 1

int n;

Console.Write("Элементов ? ");

n = Convert.ToInt32(Console.ReadLine());

k = new int[n];

for (int i = 0; i < n; i++)

{

Console.Write("K[" + i + "]=");

k[i] = Convert.ToInt32(Console.ReadLine());

}

}

public arr(int p)

{ // конструктор 2

k = new int[p];

for (int i = 0; i < p; i++)

{

Console.Write("K[" + i + "]=");

k[i] = Convert.ToInt32(Console.ReadLine());

}

}

public void output()

{

Console.WriteLine();

Console.WriteLine("Elements of Array");

for (int i = 0; i < k.Length; i++)

Console.WriteLine("K[" + i + "]=" + k[i]);

} }

На его базе можно построить классы обработки массивов. В нашем случае – нахождение суммы. Класс-наследник включает все данные своего предка (за исключением данных с атрибутом доступа private). Наследуются по общим правилам и индексаторы и свойства, а также методы перегрузки операторов.

class proc1 : arr // задаем базовый класс arr

{

int q;

public proc1()

{ // конструктор класса наследника

Console.Write("Граница ");

q = Convert.ToInt32(Console.ReadLine());

}

public int sum()

{

int s = 0;

for (int i = 0; i < k.Length; i++)

if (k[i] > q) s += k[i];

return s;

} }

Использование созданных классов

class Program

{

static void Main(string[] args)

{

proc1 My = new proc1(); //1

int s1;

My.output(); // обращение к методу предка

s1=My.sum(); // обращение к собственному методу,

// аналогично можно обращаться и к свойствам предка

Console.WriteLine("Summa= " + s1);

Console.ReadLine();

} }

При создании экземпляра класса, имеющего предка, (строка // 1) запускаются все конструкторы: в первую очередь конструктор базового класса и затем конструктор класса – наследника. В нашем случае это означает, что будет осуществлен ввод сначала массива и вслед за ним – границы. При наличии большего количества уровней наследования подряд будут запущены конструкторы всех уровней иерархии, начиная с базового.

Если конструкторы не имеют формальных параметров, то при этом никаких проблем не возникает: каждый конструктор независимо от других выполняет свои операторы. Осталось решить вопрос: как обеспечить передачу параметра (ов) конструктору класса – предка, в нашем случае конструктору 2. Проще всего это выполнить с помощью списка инициализации в конструкторе класса – наследника.

public proc1(int k1, int k2): base(k1)

{

q = k2;

}

Запись base(k1)означает, что конструктору базового класса в качестве фактического параметра будет передано значение к1. Пример главной функции в этом случае:

static void Main(string[] args)

{

int s1;

proc1 Myaa = new proc1(6, 20); //обращение к

// конструктору с параметрами

Myaa.output();

s1=Myaa.sum();

Console.WriteLine("Summa= " + s1);

Console.ReadLine();

}

Лучше всего придерживаться следующего правила: при написании конструктора класса – наследника позаботиться о параметрах непосредственного предка. Таким образом, даже при большом количестве уровней иерархии будет обеспечена согласованная работа конструкторов.