Массивы состоят из элементов одного типа. В тех случаях, когда единообразно нужно обрабатывать наборы данных, представляющих совокупность величин различного типа, рассматривая их как единое целое, целесообразно использовать структуры.
Структуры определяются с помощью ключевого слова struct . Далее указывается имя структуры и в фигурных скобках определяются члены структуры. Структуры могут содержать произвольное число различных видов членов: полей, методов и др.
Классы и структуры являются двумя основными конструкциями системы общих типов. Каждая из них по сути является структурой данных, инкапсулирующей набор данных (поля) и поведение (методы). Данные и поведение являются членами класса или структуры. Их объединение в одном типе называется инкапсуляцией. Согласно принципу инкапсуляции, класс или структура может задать уровень доступности каждого из членов по отношению к коду вне класса или структуры. Уровень доступа public (открытый доступ) использован в нижеследующих примерах. Другие уровни доступа здесь не рассматриваются.
В качестве членов структур (в дальнейшем классов) в настоящем пособии будут использоваться только поля и методы.
Поле – это переменная, объявленная в структуре. У поля есть имя и тип. Метод – это функция, определенная в структуре.
Рассмотрим определение структуры, в которой содержатся два поля разных типов:
struct Sportsmen
{
public string famile;
public int rez;
}
Здесь описана структура с именем Sportsmen с двумя полями: famile типа string и rez типа int . Описание структуры располагается вне метода Main. В связи с этим уровень доступа к полям установлен максимальный (public – открытый доступ), что дает возможность доступа к полям из метода Main.
Экземпляр структуры создается в методе Main как обычно указанием типа перед именем переменной:
Sportsmen temp;
Далее в поля этой переменной можно поместить значения (инициализировать поля структуры). Для доступа к полю нужно указать имя переменной и после точки имя поля. Например,
using System;
class Program
{
struct Sportsmen
{
public string famile;
public int rez;
}
static void Main()
{
Sportsmen temp;
temp.famile = "Иванов";
temp.rez = 77;
Console.WriteLine(
"Фам {0}\t Результат {1:f2}",
temp.famile, temp.rez);
Console.ReadKey();
}
}
Или
using System;
struct Sportsmen
{
public string famile;
public int rez;
}
class Program
{
static void Main()
{
Sportsmen temp;
temp.famile = "Иванов";
temp.rez = 7;
Console.WriteLine(
"Фам {0}\t Результат {1:f2}",
temp.famile, temp.rez);
Console.ReadKey();
}
}
Объявление массива структур. Например,
Sportsmen[] sp = new Sportsmen[5];
Здесь объявлен массив sp из пяти элементов, каждый из которых содержит два поля.
Использование структур делает представление данных более компактным и наглядным. Структуры можно пересылать одну в другую, если они идентичны. Возможен, например, оператор
sp[0] = temp;
Пример.
using System;
struct Sportsmen
{
public string famile;
public int rez;
}
class Program
{
static void Main()
{
Sportsmen temp;
temp.famile = "Иванов";
temp.rez = 17;
Sportsmen[] sp = new Sportsmen[5];
sp[0] = temp;
Console.WriteLine("Фам {0}\t Результат {1:f2}",
sp[0].famile, sp[0].rez);
Console.ReadKey();
}
}
Пример 4.1. Протокол соревнований по прыжкам в высоту содержит список фамилий и результатов (одна попытка) в порядке стартовых номеров. Получить итоговую таблицу, содержащую фамилии и результаты в порядке занятых мест. Количество спортсменов не более 30. Для размещения исходных данных используется массив структур. Структура содержит информацию – фамилия и результат спортсмена. Массив структур является в данном случае одномерным массивом и для его обработки можно использовать типовые алгоритмы, рассмотренные в гл. 3:
using System;
struct Sportsmen
{
public string famile;
public double rez;
}
class Program
{
static void Main()
{
Sportsmen[] sp = new Sportsmen[5];
string[] s = new string[] {
"Иванов", "Петров", "Сидоров",
"Кузнецов", "Макаров" };
double[] r = new double[] { 1.50,
1.55, 1.47, 1.46, 1.54 };
for (int i = 0; i < sp.Length; i++)
{
sp[i].famile = s[i];
sp[i].rez = r[i];
Console.WriteLine(
"Фамилия {0} \t Результат {1:f2}",
sp[i].famile, sp[i].rez);
}
//Упорядочение по результатам
for (int i = 0; i < sp.Length - 1; i++)
{
double amax = sp[i].rez;
int imax = i;
for (int j = i + 1; j < sp.Length; j++)
{
if (sp[j].rez > amax)
{
amax = sp[j].rez;
imax = j;
}
}
Sportsmen temp;
temp = sp[imax];
sp[imax] = sp[i];
sp[i] = temp;
}
Console.WriteLine();
for (int i = 0; i < sp.Length; i++)
{
Console.WriteLine(
"Фамилия {0} \t Результат {1:f2}",
sp[i].famile, sp[i].rez);
}
Console.ReadKey();
}
}
Здесь исходные данные первоначально заданы в двух массивах: фамилии в массиве s, результаты – в массиве r. Далее этими данными заполняются поля структуры.
В результат будет выведено:
Вопросы для самопроверки
1. Структура. Поля структуры. Члены структуры. Объявление структуры. Доступ к отдельным полям структуры.
2. Создание экземпляра структуры. Инициализация полей структуры.
3. Операции со структурами.
4. Преимущества использования структур.
5. Массивы структур и их обработка.
Задание для самостоятельного выполнения
1. На основании результатов соревнований по прыжкам в длину (фамилии и результаты трех попыток) составить итоговый протокол соревнований, считая, что в зачет идет лучший результат.
2. Результаты соревнований по прыжкам в длину определяются по сумме двух попыток. В протоколе для каждого участника указываются: фамилия, общество, результаты первой и второй попыток. Вывести протокол в виде таблицы с заголовком в порядке занятых мест.
3. Составить программу для обработки результатов кросса на 500 м для женщин. В кроссе участвуют не более 100 студенток. Для каждой участницы ввести фамилию, группу, фамилию преподавателя, результат. Получить результирующую таблицу, упорядоченную по результатам, в которой содержится также информация о выполнении норматива. Определить суммарное количество участниц, выполнивших норматив.
4. Радиокомпания провела опрос слушателей (не более 500) по вопросу: «Кого вы считаете человеком года?». Определить пять наиболее часто встречающихся ответов и их долей (в процентах от общего количества ответов).
5. Лыжные гонки проводятся отдельно для двух групп участников (в каждой группе не более 50 человек). Результаты соревнований заданы в виде фамилий участников и их результатов в каждой группе. Расположить результаты соревнований в каждой группе в порядке занятых мест. Объединить результаты обеих групп с сохранением упорядоченности и вывести в виде таблицы с заголовком.
6. Обработать результаты первенства по футболу (участвуют не более 15 команд). Результаты каждой игры заданы в виде названий команд и счета (количество забитых и пропущенных мячей). Сформировать таблицу очков (выигрыш – 3, ничья – 1, проигрыш – 0) и упорядочить результаты в соответствии с занятым местом. Если сумма очков у двух команд одинакова, то сравниваются разности забитых и пропущенных мячей. Вывести результирующую таблицу, содержащую место, название команды, количество очков.
7. Для формирования сборной по хоккею предварительно отобрано 30 игроков. На основании протоколов игр (не более 15) составлена таблица, в которой содержится штрафное время каждого игрока по каждой игре (2, 5 или 10 мин). Написать программу, которая составляет список кандидатов в сборную в порядке возрастания суммарного штрафного времени. Игрок, оштрафованный на 10 мин, из списка кандидатов исключается.
8. Результаты сессии содержат оценки 5 экзаменов по каждой группе. Определить средний балл для пяти групп одного потока студентов и выдать список групп в порядке убывания среднего бала. Результаты вывести в виде таблицы с заголовком.
9. Студенты одной группы (не более 25 человек) в сессию сдают четыре экзамена. Составить список студентов, средний балл которых по всем экзаменам не менее «4». Результаты вывести в виде таблицы с заголовком в порядке убывания среднего балла.
10. Результаты соревнований по прыжкам в длину определяются по сумме двух попыток. В протоколе для каждого участника указываются: фамилия, общество, результаты первой и второй попыток. Вывести протокол в виде таблицы с заголовком в порядке занятых мест.
11. В соревнованиях по прыжкам в воду принимают участие не более 20 спортсменов. Судят соревнования 7 судей. Каждый спортсмен выполняет 4 прыжка. Каждый прыжок имеет одну из шести категорий сложности, оцениваемую коэффициентом (от 2,5 до 3,5). Качество прыжка оценивается судьями по 6-балльной шкале. Далее лучшая и худшая оценки отбрасываются, остальные складываются, и сумма умножается на коэффициент сложности. Получить итоговую таблицу, содержащую фамилии спортсменов и итоговую оценку (сумму оценок по 4 прыжкам) в порядке занятых мест.
12. В соревнованиях по прыжкам со 120-метрового трамплина принимает участие не более 30 спортсменов. Судят соревнования 5 судей. Каждый судья выставляет оценку за стиль прыжка по 20-балльной шкале. Меньшая и большая оценки отбрасываются, остальные суммируются. К этой сумме прибавляются очки за дальность прыжка: 120 метров – 60 очков, за каждый метр превышения добавляются по 2 очка, при меньшей дальности отнимаются 2 очка за каждый метр. Получить итоговую таблицу соревнований, содержащую фамилию и итоговый результат для каждого участника в порядке занятых мест.
13. Группе студентов (не более 25) в результате полусеместровой аттестации были выставлены оценки по информатике, а также определено количество пропущенных занятий. Успеваемость каждого студента оценивается следующими баллами: «0» (неаттестован), «2», «3», «4» или «5». Вывести список неуспевающих (оценка «2») студентов в порядке убывания количества пропущенных ими занятий. Данные задать самостоятельно.
14. Протокол соревнований по прыжкам в воду содержит список фамилий спортсменов (не более 15) и баллы, выставленные 5 судьями по результатам 2 прыжков. Получить итоговый протокол, содержащий фамилии и результаты, в порядке занятых спортсменами мест по результатам 2 прыжков. Данные задать самостоятельно.
15. После окончания шахматного соpевнования турнирная таблица содержит фамилии участников (не более 10) и результаты сыгранных партий (выигрыш – 1 очко, ничья – 1/2 очка, проигрыш – 0 очков). Составить итоговую таблицу в порядке убывания полученных участниками очков. Данные задать самостоятельно.
4.2. Использование конструктора экземпляра и других методов при работе со структурами
Рассмотрим более подробно различные способы инициализации полей структуры, работы с отдельными экземплярами структуры, в частности, включение методов в описание структуры на примере структуры Sportsmen (см. пример 4.1).
В примере 4.1 экземпляр (объект) структуры объявляется как обычная переменная указанием типа перед именем. В этом случае поля структуры остаются без значений, и их нельзя использовать до инициализации всех полей. Например,
using System;
namespace ConsoleApplication1
{
struct Sportsmen
{
public string famile;
public int rez;
}
class Program
{
static void Main(string[] args)
{
Sportsmen sp;
sp.famile = "Иванов";
sp.rez = 5;
Console.WriteLine(
"Фамилия {0} Результат {1:d}",
sp.famile, sp.rez);
}
}
}
Попытка вывести значения полей сразу после объявления переменной (до задания значений полям) приведет к сообщению об ошибке.
Объект структуры может быть создан и другим способом: с использованием оператора new:
Sportsmen sp = new Sportsmen();
В этом случае при создания объекта вызывается соответствующий конструктор экземпляра, который выполняет инициализацию полей нулями. Значения полей будут следующими:
Вывод значений полей в этом случае не приведет к ошибке.
Конструктор экземпляра – это метод с тем же именем, что и структура, вызываемый оператором new. Конструктор может иметь параметры, его можно описать при определении структуры и использовать для инициализации полей отдельных объектов структуры в более компактной и наглядной форме.
Рассмотрим пример 4.1 и в описание структуры включим еще один член: конструктор с параметрами.
{
public string famile;
public double rez;
public Sportsmen(string famile1, double rez1)
{
famile = famile1;
rez = rez1;
}
}
Теперь, если объявлен массив структур
Sportsmen[] sp = new Sportsmen[5];
то задание полей элементов этого массива можно с использованием конструктора выполнить следующим образом:
sp[0] = new Sportsmen("Иванов", 1.50);
sp[1] = new Sportsmen("Петров", 1.55);
sp[2] = new Sportsmen("Сидоров", 1.47);
sp[3] = new Sportsmen("Кузнецов", 1.46);
sp[4] = new Sportsmen("Макаров", 1.54);
При вызове конструктора оператором new на место его первого параметра передается соответствующая фамилия, которая присваивается полю famile, а на место второго параметра – результат, который присваивается полю rez соответствующего экземпляра структуры (элементу массива структур).
Внутри конструктора могут выполняться и вычисления, необходимые для определения отдельных полей.
Пусть, например, каждый спортсмен выполняет две попытки (rez1, rez2) и окончательный результат определяется суммой двух попыток (rez = rez1 + rez2). Описание структуры в этом случае может быть следующим:
struct Sportsmen
{
public string famile;
public double rez1, rez2, rez;
public Sportsmen(string famile1,
double rezz1, double rezz2)
{
famile = famile1;
rez1 = rezz1;
rez2 = rezz2;
rez = rez1 + rez2;
}
}
Замечания.
1. Имена параметров конструктора не должны совпадать с именами полей структуры. Если они совпападают, то нужно использовать ключевое слово this. Например:
struct Sportsmen
{
public string famile;
public double rez1, rez2, rez;
public Sportsmen(string famile,
double rez1, double rez2)
{
this.famile = famile;
this.rez1 = rez1;
this.rez2 = rez2;
rez = rez1 + rez2;
}
}
2. В конструкторе должны быть определены все поля структуры одним из двух способов: либо присваиванием значения передаваемого конструктору аргумента, либо вычислением с использованием значений уже определенных полей.
Следующая программа реализует решение задачи примера 4.1 для случая двух попыток с использованием конструктора:
using System;
struct Sportsmen
{
public string famile;
public double rez1, rez2, rez;
public Sportsmen(string famile1,
double rezz1, double rezz2)
{
famile = famile1;
rez1 = rezz1;
rez2 = rezz2;
rez = rez1 + rez2;
}
}
class Program
{
static void Main(string[] args)
{
Sportsmen[] sp = new Sportsmen[5];
sp[0] = new Sportsmen("Иванов", 1.50, 1.52);
sp[1] = new Sportsmen("Петров", 1.55, 1.8);
sp[2] = new Sportsmen("Сидоров", 1.47, 1.5);
sp[3] = new Sportsmen("Кузнецов", 1.46, 1.43);
sp[4] = new Sportsmen("Макаров", 1.54, 1.44);
for (int i = 0; i < sp.Length; i++)
{
Console.WriteLine(
"Фамилия {0}\t Результат {1:f2}",
sp[i].famile, sp[i].rez);
}
Console.ReadKey();
}
}
Теперь при выполнении, например, оператора
sp[0] = new Sportsmen("Иванов", 1.50, 1.52);
в поле rez будет помещаться сумма 3,02.
Результат выполнения программы:
Помимо конструктора описание структуры может содержать и другие методы (см. гл. 5). Например, в описании структуры
struct Sportsmen
{
public string famile;
public double rez;
public double factor(int i)
{
return i * rez;
}
}
описан метод factor для умножнния результата rez на коэффициент i.
После объявления в методе Main объекта структуры этот метод может быть вызван указанием имени объекта структуры и после точки имени метода и его аргумента в круглых скобках:
Sportsmen sp;
sp.famile = "Иванов";
sp.rez = 5.2;
double rez1 = sp.factor(3);
Console.WriteLine(rez1);
Console.ReadKey();
В результате переменная rez1 получит значение 15,6.
В описании структуры может быть описан также статический метод. Статический метод не применяется к конкретному объекту структуры, а относится ко всей структуре, и доступ к нему осуществляется по имени структуры, а не экземпляра (объекта) структуры (см. гл. 5).
В качестве примера рассмотрим описанную выше структуру и добавим в ее описание статический метод для подсчета количества спортсменов. Этот метод будет прибавлять к общему количеству 1 после ввода данных для очередного спортсмена:
struct Sportsmen
{
public string famile;
public double rez;
public static int sportsmenCounter = 0;
public static int AddSportsmen()
{
return sportsmenCounter = sportsmenCounter + 1;
}
}
Если в методе Main имеются операторы
Sportsmen sp;
sp.famile = "Иванов";
sp.rez = 5.2;
Sportsmen.AddSportsmen();//Вызов статического метода
Console.WriteLine (Sportsmen.sportsmenCounter);
sp.famile = "Петров";
sp.rez = 7.4;
Sportsmen.AddSportsmen();
Console.WriteLine(Sportsmen.sportsmenCounter);
то в результате их выполнения первым оператором WriteLine будет выведено 1, а вторым – 2.
Пример 4.2. Студенты одной группы (не более 25 человек) в сессию сдают четыре экзамена. Составить список студентов, средний балл которых по всем экзаменам не менее «4». Результаты вывести в виде таблицы с заголовком в порядке убывания среднего балла:
using System;
struct Struct1
{
public string famile;
public double[] x;
public double sred;
public Struct1(string famile1, double[] x1)
{
sred = 0;
famile = famile1;
x = x1;
for (int i = 0; i < 4; i++)
{
sred += x[i];
}
sred /= 4;// sred = sred/4
}
}
class Program
{
static void Main(string[] args)
{
Struct1[] cl = new Struct1[3];
cl[0] = new Struct1("Иванов",
new double[] { 3.0, 5.0, 2.0, 3.0 });
cl[1] = new Struct1("Петров",
new double[] { 5.0, 4.0, 5.0, 3.0 });
cl[2] = new Struct1("Сидоров",
new double[] { 5.0, 4.0, 5.0, 5.0 });
for (int i = 0; i < cl.Length; i++)
Console.WriteLine(
"Фамилия {0}\t Средний балл {1,4:f2}",
cl[i].famile, cl[i].sred);
for (int i = 0; i < cl.Length - 1; i++)
{
double amax = cl[i].sred;
int imax = i;
for (int j = i + 1; j < cl.Length; j++)
{
if (cl[j].sred > amax)
{
amax = cl[j].sred;
imax = j;
}
}
Struct1 temp;
temp = cl[imax];
cl[imax] = cl[i];
cl[i] = temp;
}
Console.WriteLine();
for (int i = 0; i < cl.Length; i++)
{
if (cl[i].sred >= 4)
Console.WriteLine(
"Фамилия {0}\t "
+ "Средний балл {1,4:f2}",
cl[i].famile, cl[i].sred);
}
}
}
Здесь членом структуры является конструктор с двумя параметрами, соответствующими двум полям структуры, которые будут заполняться при вызове конструктора для каждого объекта структуры. При этом значение третьего поля вычисляется в конструкторе с использованием значений элементов массива, являющегося вторым полем структуры.
Вопросы для самопроверки
1. Создание объекта структуры при помощи конструктора. Что такое конструктор экземпляра.
2. Особенности инициализации полей структуры при использовании конструктора экземпляра.
3. Различные способы задания значений полей структуры при использовании конструктора.
4. Возможность использования методов, как членов структуры.
Задания для самостоятельного выполнения
Выполнить задания п. 4.1 с использованием конструктора и различных вариантов объявления структуры (внутри класса, вне класса).