Структуры данных, коллекции и классы-прототипы
В этой главе приводится обзор основных структур данных, используемых в программировании, и их реализация в библиотеке .NET в виде коллекций. Кроме того, описываются средства, добавленные в версию С# 2.0, — классы-прототипы (generics), частичные типы (partial types) и обнуляемые типы (nullable types).
Любая программа предназначена для обработки данных, от способа организации которых зависит ее алгоритм. Для разных задач необходимы различные способы хранения и обработки данных, поэтому выбор структур данных должен предшествовать созданию алгоритмов и основываться на требованиях к функциональности и быстродействию программы.
Наиболее часто в программах используются следующие структуры данных: массив, список, стек, очередь, бинарное дерево, хеш-таблица, граф и множество. Далее даны краткие характеристики каждой их этих структур.
Массив — это конечная совокупность однотипных величин. Массив занимает непрерывную область памяти и предоставляет прямой, или произвольный, доступ к своим элементам по индексу. Память под массив выделяется до начала работы с ним и впоследствии не изменяется.
В списке каждый элемент связан со следующим и, возможно, с предыдущим. В первом случае список называется односвязным, во втором — двусвязным. Также применяются термины однонаправленный и двунаправленный. Если последний элемент связать указателем с первым, получается кольцевой список. Количество элементов в списке может изменяться в процессе работы программы. Каждый элемент списка содержит ключ, идентифицирующий этот элемент. Ключ обычно бывает либо целым числом, либо строкой и является частью данных хранящихся в каждом элементе списка. В качестве ключа в процессе работы со списком могут выступать разные части данных. Например, если создается список из записей, содержащих фамилию, год рождения, стаж работы и пол, любая часть записи может выступать в качестве ключа: при упорядочивании списка по алфавиту ключом будет фамилия, а при поиске, например, ветеранов труда ключом можно сделать стаж. Ключи разных элементов списка могут совпадать.
Над списками можно выполнять следующие операции:
□ добавление элемента в конец списка;
□ чтение элемента с заданным ключом;
□ вставка элемента в заданное место списка;
□ удаление элемента с заданным ключом;
□ упорядочивание списка по ключу.
Список не обеспечивает произвольный доступ к элементу, поэтому при выполнении операций чтения, вставки и удаления выполняется последовательный перебор элементов, пока не будет найден элемент с заданным ключом. Для списков большого объема перебор элементов может занимать значительное время, поскольку среднее время поиска элемента пропорционально количеству элементов в списке.
Стек — это частный случай однонаправленного списка, добавление элементов в который и выборка из которого выполняются с одного конца, называемого вершиной стека. Другие операции со стеком не определены. При выборке элемент исключается из стека. Говорят, что стек реализует принцип обслуживания LIFO (Last In — First Out, последним пришел — первым ушел). Стек проще всего представить себе как закрытую с одного конца узкую трубу, в которую бросают мячики. Достать первый брошенный мячик можно только после того, как вынуты все остальные. Стеки широко применяются в системном программном обеспечении, компиляторах, в различных рекурсивных алгоритмах. Очередь — это частный случай однонаправленного списка, добавление элементов в который выполняется в один конец, а выборка — из другого конца. Другие операции с очередью не определены. При выборке элемент исключается из очереди. Говорят, что очередь реализует принцип обслуживания FIFO (First In — First Out, первым пришел — первым ушел).
Очередь проще всего представить себе, постояв в ней час-другой. В программировании очереди применяются, например, в моделировании, диспетчеризации задач операционной системой, буферизован ном вводе-выводе.
Бинарное дерево — это динамическая структура данных, состоящая из узлов, каждый из которых содержит помимо данных не более двух ссылок на различные бинарные поддеревья. На каждый узел имеется ровно одна ссылка. Начальный узел называется корнем дерева.
Пример бинарного дерева приведен на рис. 13.1 (корень обычно изображается сверху). Узел, не имеющий поддеревьев, называется листом. Исходящие узлы называются предками, входящие - потомками. Высота дерева определяется количеством уровней, на которых располагаются его узлы.
Если дерево организовано таким образом, что для каждого узла все ключи его левого поддерева Меньше ключа этого узла, а все ключи его правого поддерева — больше, оно называется деревом поиска. Одинаковые ключи не допускаются.
В дереве поиска можно найти элемент по ключу, двигаясь от корня и переходя на левое или правое поддерево в зависимости от значения ключа в каждом узле. Такой поиск гораздо эффективнее поиска по списку, поскольку время поиска определяется высотой дерева, а она пропорциональна двоичному логарифму количества узлов. Впрочем, скорость поиска в значительной степени зависит от порядка формирования дерева: если на вход подается упорядоченная или почти упорядоченная последовательность ключей, дерево вырождается в список. Для Ускорения поиска применяется процедура балансировки дерева, формирующая Дерево, поддеревья которого различаются не более чем на один элемент. Бинарные деревья применяются для эффективного поиска и сортировки данных.
Хеш-таблица, ассоциативный массив, или словарь — это массив, доступ к элементам которого осуществляется не по номеру, а по некоторому ключу. Можно сказать, что это таблица, состоящая из пар «ключ—значение» (табл. 13.1). Хеш-таблица эффективно реализует операцию поиска значения по ключу. При этом ключ преобразуется в число (хеш-код), которое используется для быстрого нахождения нужного значения в хеш-таблице.
Преобразование выполняется с помощью хеш-функции, или функции расстановки. Эта функция обычно производит какие-либо преобразования внутреннего представления ключа, например, вычисляет среднее арифметическое кодов составляющих его символов (английское слово «hash» означает перемалывать, перемешивать). Если хеш-функция распределяет совокупность возможных ключей равномерно по множеству индексов массива, то доступ к элементу по ключу выполняется почти так же быстро, как в массиве. Если хеш-функция генерирует для разных ключей одинаковые хеш-коды, время поиска возрастает и становится сравнимым со временем поиска в списке.
ПРИМЕЧАНИЕ -------------------------------------------------------------------------------------
Грубый пример хеш-функции: когда мы обращаемся к англо-русскому словарю, мы можем использовать в качестве хеш-кода первую букву английского слова (ключа). Открыв словарь на странице, с которой начинаются слова на эту букву, выполняем последовательный поиск ключа в списке.
---------------------------------------------------.--------------------------------------------------------
Смысл хеш-функции состоит в том, чтоб отобразить более широкое множество ключей в более узкое множество индексов. При этом неизбежно возникают так называемые коллизии, когда хеш-функция формирует для двух разных элементов один и тот же хеш-код. В разных реализациях хеш-таблиц используются различные стратегии борьбы с коллизиями.
Граф — это совокупность узлов и ребер, соединяющих различные узлы. Например, можно представить себе карту автомобильных дорог как граф с городами в качестве узлов и шоссе между городами в качестве ребер. Множество реальных практических задач можно описать в терминах графов, что делает их структурой данных, часто используемой при написании программ.
Множество — это неупорядоченная совокупность элементов. Для множеств определены операции проверки принадлежности элемента множеству, включения и исключения элемента, а также объединения, пересечения и вычитания множеств. Описанные структуры данных называются абстрактными, поскольку в них не задается реализация допустимых операций.
В библиотеках большинства современных объектно-ориентированных языков программирования представлены стандартные классы, реализующие основные
абстрактные структуры данных. Такие классы называются коллекциями, или контейнерами. Для каждого типа коллекции определены методы работы с ее элементами, не зависящие от конкретного типа данных, которые хранятся в коллекции, поэтому один и тот же вид коллекции можно использовать для хранения данных различных типов. Использование коллекций позволяет сократить сроки разработки программ и повысить их надежность.
ВНИМАНИЕ--------------------------------------------------------------------------------------------------
Каждый вид коллекции поддерживает свой набор операций над данными, и быстродействие этих операций может быть разным. Выбор вида коллекции зависит от того, что требуется делать с данными в программе и какие требования предъявляются к ее быстродействию. Например, при необходимости часто вставлять и удалять элементы из середины последовательности следует использовать список, а не массив, а если включение элементов выполняется главным образом в конец или начало последовательности — очередь. Поэтому изучение возможностей стандартных коллекций и их грамотное применение являются необходимыми условиями создания эффективных и профессиональных программ.
---------------------------------------------------------------------------------------------------------------------
В библиотеке .NET определено множество стандартных классов, реализующих большинство перечисленных ранее абстрактных структур данных. Основные пространства имен, в которых описаны эти классы, — System. Col lections, System. Collections.Specialized и System.Collections.Generic (начиная с версии 2.0). В следующих разделах кратко описаны основные элементы этих пространств имен.
ПРИМЕЧАНИЕ -----------------------------------------------------------------------------------------
Класс System. Array, представляющий базовую функциональность массива, описан в разделе «Класс System.Array» (см. с. 133).
--------------------------------------------------------------------------------------------------------------
Пространство имен System. Collections
В пространстве имен System. Collections определены наборы стандартных коллекций и интерфейсов, которые реализованы в этих коллекциях. В табл. 13.2 приведены наиболее важные интерфейсы, часть из которых мы уже изучали в разделе «Стандартные интерфейсы .NET»
(см. с. 198).
Таблица 13.2 {продолжение)
_________________________________________________________________________
Интерфейс Назначение
_________________________________________________________________________
I Enumerable Возвращает интерфейс I Enumerator для указанного объекта
I Enumerator Обычно используется для поддержки оператора foreach
в отношении объектов
IHashCodeProvider Возвращает хеш-код для реализации типа с применением
выбранного пользователем алгоритма хеширования
I List Поддерживает методы добавления, удаления и
индексирования элементов в списке объектов
___________________________________________________________________________
В табл. 13.3 перечислены основные коллекции, определенные в пространстве
System. Collections.
Пространство имен System.Col lections.Specialized включает специализированные коллекции, например коллекцию строк StringCollection и хеш-таблицу со строковыми ключами StringDictionary.
В качестве примера стандартной коллекции рассмотрим класс ArrayList.
Класс ArrayList
Основным недостатком обычных массивов является то, что объем памяти, необходимый для хранения их элементов, должен быть выделен до начала работы с массивом. Класс ArrayLi st позволяет программисту не заботиться о выделений памяти и хранить в одном и том же массиве элементы различных типов.
По умолчанию при создании объекта типа ArrayList строится массив из 16 элементов типа object. Можно задать желаемое количество элементов в массиве, передав его в конструктор или установив в качестве значения свойства Capacity, например:
Основные методы и свойства класса ArrayList перечислены в табл. 13.4.
Класс ArrayList реализован через класс Array, то есть содержит закрытое поле этого класса. Поскольку все типы в С# являются потомками класса object, массив может содержать элементы произвольного типа. Даже если в массиве хранятся обычные целые числа, то есть элементы значимого типа, внутренний класс является массивом ссылок на экземпляры типа object, которые представляют собой упакованный тип-значение. Соответственно, при занесении в массив выполняется упаковка, а при извлечении — распаковка элемента. Это не может не сказаться на быстродействии алгоритмов, использующих ArrayList.
Если при добавлении элемента в массив оказывается, что фактическое количество элементов массива превышает его емкость, она автоматически удваивается, то есть происходит повторное выделение памяти и переписывание туда всех существующих элементов.
Пример занесения элементов в экземпляр класса ArrayList:
arrl.Add( 123 );
arrl.AddC -2 );
arrl.AdcK "Вася" );
Доступ к элементу выполняется по индексу, однако при этом необходимо явным образом привести полученную ссылку к целевому типу, например:
int. a = (int) arrl[0];
int. b = (int) arri[l];
string s = (string) arrl[2]:
Попытка приведения к типу, не соответствующему хранимому в элементе, вызывает генерацию исключения Inva.lidCastExceptIon.
Для повышения надежности программ применяется следующий прием: экземпляр класса ArrayList объявляется закрытым полем класса, в котором необходимо хранить коллекцию значений определенного типа, а затем описываются методы работы с этой коллекцией, делегирующие свои функции методам ArrayList. Этот способ иллюстрируется в листинге 13.1, где. создается класс для хранения объектов типа Monster и производных от него. По сравнению с аналогичным листингом из раздела «Виртуальные методы» (см. с. 178), в котором используется обычный массив, у нас появилась возможность хранить в классе Stado произвольное количество элементов.
Листинг 13.1. Коллекция объектов
using System:
using System.Col lections:
namespace ConsoleApplicationl
{
class Monster { ... }
class Daemon : Monster { ... }
class Stado : IEnumerable
{
private ArrayList list;
public StadoO { list = new ArrayListO; }
public void Add( Monster m ) { list.Add( m ) ; }
public void RemoveAt( int i ) { list.RemoveAt( i ); }
public void Clear( ) { list.ClearO; } public I Enumerator GetEnumerator( )
{ return list.GetEnumerator( ); }
}
class Classl
{ static void MainO
{
Stado stado = new Stado( ) ;
stado.Add( new Monster( "Monia" ) );
stado.Add( new Monster( "Monk" ) );
stado.Add( new Daemon ( "Dimon", 3 ) );
stado.RemoveAt( 1 );
foreach ( Monster x in stado ) x.Passport( ) ;
}
}
Результат работы программы:
Monster Monia health = 100 ammo = 100
Daemon Dimon health = 100 ammo = 100 brain = 3
Недостатком этого решения является то, что для каждого метода стандартной коллекции приходится описывать метод-оболочку, вызывающий стандартный метод. Хотя это и несложно, но несколько неизящно. В С#, начиная с версии 2.0, появились классы-прототипы (generics), позволяющие решить эту проблему. Мы рассмотрим их в следующем разделе.
Многие алгоритмы не зависят от типов данных, с которыми они работают. Простейшими примерами могут служить сортировка и поиск. Возможность отделить алгоритмы от типов данных предоставляют классы-прототипы (generics) — классы, имеющие в качестве параметров типы данных. Чаще всего эти классы применяются для хранения данных, то есть в качестве контейнерных классов, или коллекций.
ПРИМЕЧАНИЕ_____________________________________________________________
Английский термин «generics» переводится в нашей литературе по-разному: универсальные классы, родовые классы, параметризованные классы, обобщенные классы, шаблоны, классы-прототипы и даже просто генерики. Наиболее адекватными мне кажутся варианты «классы-прототипы» и «параметризованные классы», поэтому в последующем изложении в основном используются именно эти термины, хотя и их точными не назовешь.____________________________________________________________________
Во вторую версию библиотеки .NET добавлены параметризованные коллекции для представления основных структур данных, применяющихся при создании программ, — стека, очереди, списка, словаря и т. д. Эти коллекции, расположенные в пространстве имен System.Collections.Generic, дублируют аналогичные коллекции пространства имен System. Col lections, рассмотренные в разделе «Пространство имен System.Collections» (см. с. 295). В табл. 13.5 приводится соответствие между обычными и параметризованными коллекциями библиотеки .NET (параметры, определяющие типы данных, хранимых в коллекции, указаны в угловых скобках).
Таблица 13.5. Параметризованные коллекции библиотеки .NET версии 2.0
_____________________________________________________________________________
Класс-прототип (версия 2.0) Обычный класс
_____________________________________________________________________________
Comparer<T > Comparer
Dictionary<K,T > HashTable
LinkedList<T> —
List<T> ArrayList
Queue<T > Queue
SortedDictionary<K,T > SortedList
Stack<T > Stack
_____________________________________________________________________________
У коллекций, описанных в библиотеке .NET версий 1.0 и 1.1, есть два основных недостатка, обусловленных тем, что в них хранятся ссылки на тип object:
¨ в одной и той же коллекции можно хранить элементы любого типа, следовательно, ошибки при помещении в коллекцию невозможно проконтролировать на этапе компиляции, а при извлечении элемента требуется его явное преобразование;
¨ при хранении в коллекции элементов значимых типов выполняется большой объем действий по упаковке и распаковке элементов, что в значительной степени снижает эффективность работы.
Параметром класса-прототипа является тип данных, с которым он работает. Это избавляет от перечисленных недостатков. В качестве примера рассмотрим применение универсального «двойника» класса ArrayList — класса List<T> — для хранения коллекции объектов классов Monster и Daemon, которые разрабатывались в главах 5 и 8, а также для хранения целых чисел.
Листинг 13.2. Использование универсальной коллекции List<T>
using System;
using System.Col 1ections.Generiс;
using System.Text;
namespace ConsoleApplicationl
{
using MonsterLib;
class Program
{
static void Main( )
{
List<Monster> stado = new List<Monster>();
stado.Add( new Monster( "Monia" ) );
stado.Add( new Monster( "Monk" ) );
stado.Add( new Daemon ( "Dimon", 3 ) );
foreach ( Monster x in stado ) x.Passport;
List<int> lint = new List<int>();
lint.AddC 5 ); lint.AddC 1 ); 1 int.Add( 3 );
lint.Sort();
int a = lint[2];
Console.WriteLine( a );
foreach ( int x in lint ) Console.Write( x + " " );
}
Результат работы программы:
Monster Monia health = 100 ammo =100
Monster Monk health = 100 ammo = 100
Daemon Dimon health = 100 ammo = 100 brain = 3
5
13 5
В листинге 13.2 две коллекции. Первая (stado) содержит элементы пользовательских классов, которые находятся в библиотеке MonsterLib.dll, созданной в предыдущей главе. В коллекции, для которой объявлен тип элементов Monster, благодаря полиморфизму можно хранить элементы любого производного класса, но не элементы других типов.
Казалось бы, по сравнению с обычными коллекциями это ограничение, а не универсальность, однако на практике коллекции, в которых действительно требуется хранить значения различных, не связанных межу собой типов, почти не используются. Достоинством же такого ограничения является то, что компилятор может выполнить контроль типов во время компиляции, а не выполнения программы, что повышает ее надежность и упрощает поиск ошибок. Коллекция 1 i nt состоит из целых чисел, причем для работы с ними не требуются ни операции упаковки и распаковки, ни явные преобразования типа при получении элемента из коллекции, как это было в обычных коллекциях (см. листинг 13.1).
Классы-прототипы называют также родовыми или шаблонными, поскольку они представляют собой образцы, по которым во время выполнения программыстроятся конкретные классы. При этом сведения о классах, которые являются параметрами классов-прототипов, извлекаются из метаданных.
ВНИМАНИЕ___________________________________________________________
Использование стандартных параметризованных коллекций для хранения и обра ботки данных является хорошим стилем программирования, поскольку позволяете сократить сроки разработки программ и повысить их надежность. Рекомендуется тщательно изучить по документации свойства и методы этих классов и выбирать наиболее подходящие в зависимости от решаемой задачи.
_______________________________________________________________________
В листинге 13.3 приведен еще один пример применения параметризованных коллекций. Программа считывает содержимое текстового файла, разбивает его на слова и подсчитывает количество повторений каждого слова в тексте. Для хранения слов и числа их повторений используется словарь Dictionary<T,K>. У этого класса два параметра: тип ключей и тип значений, хранимых в словаре. В качестве ключей используются слова, считанные из файла, а значения представляют собой счетчики целого типа, которые увеличиваются на единицу, когда слово встречается в очередной раз.
Пусть исходный файл text. txt содержит строки
Ехал Грека через реку. Видит Грека, в реке рак.
Сунул Грека в реку руку, рак за руку Греку цап!
Тогда результат работы программы будет выглядеть так:
Ехал 1
Грека 3
через 1
реку 2
4
Видит 1
в 2
реке 1
рак 2
Сунул 1
руку 2
за 1
Греку 1
цап 1
Несколько пояснений к программе. В операторе 1 открывается текстовый файл, длина которого не должна превышать 32 767 символов, потому что в операторе 2 все его содержимое считывается в отдельную строку.
ПРИМЕЧАНИЕ_______________________________________________________________
Конечно, для реальной работы такой способ не рекомендуется. Кроме того, для файлов, открываемых для чтения, программа обязательно должна обрабатывать исключение FileNotFoundException (см. главу 11).
В операторе 3 задается массив разделителей, передаваемый в качестве параметра методу Spl it, формирующему массив строк, каждая из которых содержит отдельное слово исходного файла. Этот массив используется для инициализации экземпляра words класса List<string>. Применение стандартного класса позволяет не заботиться о выделении места под массив слов.
Оператор 5 описывает словарь, а в цикле 6 выполняется его заполнение путем просмотра списка слов words. Если слово встречается впервые, в значение, соответствующее слову как ключу, заносится единица. Если слово уже встречалось, значение увеличивается на единицу.
В цикле 7 выполняется вывод словаря путем просмотра всех его ключей (для этого используется свойство словаря Keys, возвращающее коллекцию ключей) и выборки соответствующих значений.
Метод Split не очень интеллектуален: он рассматривает пробел, расположенный после знака препинания, как отдельное слово. Для более точного разбиения на слова используются регулярные выражения, которые рассматриваются в главе 15.
ПРИМЕЧАНИЕ_________________________________________________________
Обратите внимание на то, насколько использование стандартных коллекций сокращает исходный текст программы. Конечно, на тщательное изучение их возможностей требуется много времени, однако это окупается многократно.
________________________________________________________________________
Для полноты картины следует добавить, что наряду с параметризованными классами в пространстве имен System. Col lections. Generic описаны параметризованные интерфейсы, перечисленные в табл. 13.6.
Таблица 13.6. Параметризованные интерфейсы библиотеки .NET версии 2.0
_____________________________________________________________________________
Параметризованный интерфейс (версия 2.0) Обычный интерфейс
_____________________________________________________________________________
ICollection<T> iCollection
IComparab1e<T> IComparable IDictionary<K,T> I Dictionary
IEnumerable<T> IEnumerable
IEnumerator<T> Enumerator
IList<T > IList
Создание класса-прототипа
Язык С# позволяет создавать собственные классы-прототипы и их разновидности — интерфейсы, структуры, делегаты и события, а также обобщенные (generic) методы обычных классов.
Рассмотрим создание класса-прототипа на примере стека, приведенном в спецификации С#. Параметр типа данных, которые хранятся в стеке, указывается в угловых скобках после имени класса, а затем используется таким же образом, как и обычные типы:
public class Stack<T>
{
Т[] items;
int count;
public void Push( T item ){ ... } . // помещение в стек
public T Pop О { ... } // извлечение из стека
}
При использовании этого класса на место параметра Т подставляется реальный тип, например i nt:
Stack<int> stack = new Stack<int>( );
stack.Push( 3 );
int x = stack.PopO;
Тип Stack<int> называется сконструированным типом (constructed type). Этот тип создается во время выполнения программы при его первом упоминании в программе. Если в программе встретится класс Stack с другим значимым типом, например double, среда выполнения создаст другую копию кода для этого типа. Напротив, для всех ссылочных типов будет использована одна и та же копия кода, поскольку работа с указателями на различные типы выполняется одинаковым образом. Создание конкретного экземпляра класса-прототипа называется инстанцированием.
Класс-Прототип может содержать произвольное количество параметров типа. Для каждого из них могут быть заданы ограничения (constraints), указывающие, каким требованиям должен удовлетворять аргумент, соответствующий этому параметру, например, может быть указано, что это должен быть значимый тип или тип, который реализует некоторый интерфейс.
Синтаксически ограничения задаются после ключевого слова where, например:
public class Stack<T>
where T : struct
{ ...
Здесь с помощью слова struct записано ограничение, что элементы стека должны быть значимого типа. Для ссылочного типа употребляется ключевое слово class. Для каждого типа, являющегося параметром класса, может быть задана одна строка ограничений, которая может включать один класс, а за ним — произвольное количество интерфейсов, перечисляемых через запятую.
Указание в качестве ограничения имени класса означает, что соответствующий аргумент при инстанцировании может быть именем либо этого класса, либо его потомка. Указание имени интерфейса означает, что тип-аргумент должен реализовывать данный интерфейс — это позволяет использовать внутри класса-прототипа, например, операции по перечислению элементов, их сравнению и т. п.
Помимо класса и интерфейсов в ограничениях можно задать требование, чтобы тип-аргумент имел конструктор по умолчанию без параметров, что позволяет создавать объекты этого типа-в теле методов класса-прототипа. Это требование записывается в виде выражения new( ), например:
public class EntityTable<K.E>
where К: IComparable<K>. IPersistable
where E: Entity, new( )
{
public void Add( К key, E entity )
{ ...
if ( key.CompareTo( x )< 0 ){...}
} }
В этом примере на первый аргумент класса Entity Table накладываются два ограничения по интерфейсам, а для второго аргумента задано, что он может быть только классом Entity или его потомком и иметь конструктор без параметров.
Задание ограничений позволяет компилятору выполнять более строгий контроль типов и, таким образом, избежать многих ошибок, которые иначе проявились бы только во время выполнения программы.
Для задания значений по умолчанию типизированным элементам класса-прототипа используется ключевое слово default. С его помощью элементам ссылочного типа присваивается значение null, а элементам значимого типа — 0.
Обобщенные методы
Иногда удобно иметь отдельный метод, параметризованный каким-либо типом данных. Рассмотрим этот случай на примере метода сортировки.
Известно, что «самого лучшего» алгоритма сортировки не существует. Для различных объема, диапазона, степени упорядоченности данных и распределения ключей оптимальными могут оказаться разные алгоритмы. Стандартные методы сортировки реализуют алгоритмы, которые хороши для большинства применений, но не для всех, поэтому может возникнуть необходимость реализовать собственный метод.
В листинге 13.4 приведен пример сортировки методом выбора. Алгоритм состоит в том, что сначала выбирается наименьший элемент массива и меняется местами с первым элементом, затем просматриваются элементы, начиная со второго, и наименьший из них меняется местами со вторым элементом, и т. д., всего п - 1 раз. На последнем проходе цикла при необходимости меняются местами предпоследний и последний элементы массива.
Листинг 13.4. Сортировка выбором
using System;
using System.Col lections.Generic;
using System.Text;
namespace ConsoleApplicationi
{ class Program
{
static void Sort<T> ( ref T[] a ) //1
where T : IComparable<T> //2
{
T buf;
int n = a.Length;
for ( int i = 0: i < n - 1: ++i )
{
int im = i;
for ( int j - i + 1; j < n; ++j ).
if ( a[j].CompareTo(a[im]) < 0 ) im = j; //3
buf = a[i]; a[i] = a[im]; a[im] = buf;
}
} static void MainO
{
1nt[] a =,{ 1, 6, 4, 2, 7, 5, 3 };
Sort<int>( ref a ); //4
foreach ( int el em in a ) Console.WriteLine( el em );
doubled b= { 1.1, 5.2, 5.21, 2, 7, 6, 3 };.
Sort( ref b );. //5
foreach ( double elem in b ) Console.WriteLine( elem );
string[] s = { "qwe", "qwer", "df", "asd" };
Sort( ref s ): //6
foreach ( string elem in s ) Console.MriteLine( elem );
}
}
Метод параметризован типом, на который накладывается ограничение (оператор 2), чтобы объекты класса-аргумента можно было сравнивать друг с другом с помощью метода СотрагеТо, использованного в операторе 3.
В главной программе (методе Main) метод Sort вызывается двумя способами: с явным указанием параметра-типа (оператор 4) и без указания параметра (операторы 5 и 6). Во втором случае компилятор по типу переданного в метод параметра самостоятельно определяет, какой именно тип используется при инстанцировании.
ПРИМЕЧАНИЕ _____________________________________________________________
Параметры-типы могут использоваться в списке параметров, возвращаемом типе и в теле универсального метода.
________________________________________________________________________
Итак, параметризованные типы и методы позволяют:
¨ описывать способы хранения и алгоритмы обработки данных независимо от типов данных;
¨ выполнять контроль типов во время компиляции, а не исполнения программы;
¨ увеличить скорость обработки данных за счет исключения операций упаковки, распаковки и преобразования типа.
Как уже упоминалось, помимо классов-прототипов и обобщенных методов можно описать параметризованные интерфейсы, структуры и делегаты.
В помощью параметризованных интерфейсов можно определить список функций, которые могут быть реализованы различным образом для разных классов, реализующих эти интерфейсы. Параметризованные интерфейсы можно реализовывать в классе-прототипе, используя в качестве аргументов интерфейса параметры типа, реализующего интерфейс, или в обычном классе, подставляя в качестве параметров интерфейса конкретные типы.
Параметризованные делегаты позволяют создавать обобщенные алгоритмы, логику которых можно изменять передаваемыми в качестве параметров делегатами.
Во вторую версию языка введена возможность разбивать описание типа на части и хранить их в разных физических файлах, создавая так называемые частичные типы (partial types). Это может потребоваться для классов большого объема или, что более актуально, для отделения части кода, сгенерированной средствами среды, от написанной программистам вручную. Кроме того, такая возможность облегчает отладку программы, позволяя отделить отлаженные части класса от новых.
Для описания отдельной части типа используется модификатор partial. Он может применяться к классам, структурам и интерфейсам, например:
public partial class A
{ …
}
public partial class A
{ …
}
После совместной компиляции этих двух частей получается такой же код, как если бы класс был описан обычным образом. Все части одного и того же частичного типа должны компилироваться одновременно, иными словами, добавление новой части к уже скомпилированным не допускается.
Модификатор partial не является ключевым словом и должен стоять непосредственно перед одним из ключевых слов class, struct или interface в каждой из частей. Все части определения одного класса должны быть описаны в одном и том же пространстве имен.
ПРИМЕЧАНИЕ_______________________________________________________________
Если модификатор parti а указывается для типа, описание которого состоит только из одной части, это не является ошибкой.
________________________________________________________________________
Модификаторы доступа для всех частей типа должны быть согласованными. Если хотя бы одна из частей содержит модификатор abstract или sealed, класс считается соответственно абстрактным или бесплодным.
Класс-прототип также может объявляться по частям, в этом случае во всех частях должны присутствовать одни и те же параметры типа с одними и теми же ограничениями.
Если частичный тип является наследником нескольких интерфейсов, в каждой части не требуется перечислять все интерфейсы: обычно в одной части объявляется один интерфейс и описывается его реализация, в другой части — другой интерфейс и т. д. Набором базовых интерфейсов для типа, объявленного в нескольких частях, является объединение базовых интерфейсов, определенных в каждой части.
В программировании давно существует проблема, каким образом задать, что переменной не присвоено никакого значения. Эта проблема решается разными способами. Один из способов заключается в том, чтобы присвоить переменной какое-либо значение, не входящее в диапазон допустимых для нее. Например, если величина может принимать только положительные значения, ей присваивается -1. Ясно, что для многих случаев этот подход неприменим. Другой способ — хранение логического признака, по которому можно определить, присвоено ли переменной значение. Этот способ не может использоваться, например, для значений, возвращаемых из метода.
В версии С# 2.0 указанная проблема решается введением типов специального вида, называемых обнуляемыми (nullable). Обнуляемый тип представляет собой структуру, хранящую наряду со значением величины (свойство Value) логический признак, по которому можно определить, было ли присвоено значение этой величине (свойство HasValue).
Если значение величине было присвоено, свойство имеет значение true. Если значение величины равно null, свойство HasValue имеет значение false, а попытка получить значение через свойство Val ue вызывает генерацию исключения.
Обнуляемый тип строится на основе базового типа, за которым следует символ ?, например:
int.? х = 123;
int? у = null;
if ( х.HasValue ) Console.WriteLine( x ); // вместо х можно записать х.Value
if ( y.HasValue ) Console.WriteLineC у );
Существуют явные и неявные преобразования из обнуляемых типов в обычные и обратно, при этом выполняется контроль возможности получения значения, например:
int i = 123;
int? x = i; //int --> int?
double? у = х; //int? --> double?
int? z = (int?) y; // double? --> int?
int j = (int) z; // int? --> int
Для величин обнуляемых типов определены операции отношения . Операции == и != возвращают значение true, если обе величины имеют значение null. Естественно, что значение nul 1 считается не равным любому ненулевому значению. Операции <, >, <= и >= дают в результате false, если хотя бы один из операндов имеет значение null.
Арифметические операции с величинами обнуляемых типов дают в результате nul 1, если хотя бы один из операндов равен null, например:
int? х = null;
int? у = х + 1; // у = null
Для величин обнуляемых типов введена еще одна операция — объединения ?? (null coalescing operator). Это бинарная операция, результат которой равен первому операнду, если он не равен null, и второму в противном случае. Иными словами, эта операция предоставляет замещаемое значение для null, например:
int? х = null;
int у = х ?? 0; // у = 0
х - 1:
y = х ?? 0; // у = 1
Обнуляемые типы удобно использовать при работе с базами данных и XML
Рекомендации по программированию
Алгоритм работы программы во многом зависит от способа организации ее данных, поэтому очень важно до начала разработки алгоритма выбрать оптимальные структуры данных, основываясь на требованиях к функциональности и быстродействию программы.
Для разных задач необходимы различные способы хранения и обработки данных, поэтому необходимо хорошо представлять себе как характеристики и области применения абстрактных структур данных, так и их конкретную реализацию в виде коллекций библиотеки. Изучение возможностей стандартных коллекций и их грамотное применение является необходимым условием создания эффективных и профессиональных программ, позволяет сократить сроки разработки программ и повысить их надежность.
Недостатками коллекций первых версий библиотеки .NET является отсутствие контроля типов на этапе компиляции и неэффективность при хранении элементов значимых типов. Параметризованные коллекции, появившиеся в версии 2.0 библиотеки, избавлены от этих недостатков, поэтому в программах рекомендуется использовать именно коллекции версии 2.0, выбирая .наиболее подходящие классы в зависимости от решаемой задачи.
Для реализации алгоритмов, независимых от типов данных, следует использовать классы-прототипы и обобщенные методы. Они не снижают эффективность программы по сравнению с обычными классами и методами, поскольку код для конкретного типа генерируется средой CLR во время выполнения программы. Помимо классов-прототипов и обобщенных методов можно описать параметризованные интерфейсы, структуры и делегаты.
Частичные типы удобно использовать при разработке объемных классов группой программистов и для упрощения отладки программ. Обнуляемые типы применяют для работы с данными, для которых необходимо уметь определять, было ли им присвоено значение.
Введение в программирование под Windows
В предыдущих главах мы изучали возможности языка С# на примере консольных приложений, чтобы не распылять свое внимание на изучение классов библиотеки .NET, поддерживающих стандартный графический интерфейс пользователя. Сейчас пришло время освоить принципы создания Windows-приложений и получить представление об основных классах библиотеки, которые при этом используются. В первую очередь рассмотрим основные особенности Windows как характерного и широко распространенного представителя современных операционных систем.
¨ Многозадачность — это возможность одновременно выполнять несколько приложений. Операционная система обеспечивает разделение ресурсов: каждому приложению выделяется свое адресное пространство, распределяется процессорное время и организуются очереди для доступа к внешним устройствам. Внутри приложения также можно организовать параллельное выполнение нескольких фрагментов, называемых потоками. В разных версиях Windows используются различные механизмы диспетчеризации.
¨ Независимость программ от аппаратуры. Для управления аппаратными средствами любое приложение обращается к операционной системе, что обеспечивает независимость от конкретных физических характеристик устройств: при смене устройства никакие изменения в программу вносить не требуется. Управление внешними устройствами обеспечивается с помощью драйверов.
¨ Стандартный графический интерфейс с пользователем. Основное взаимодействие с пользователем осуществляется в графическом режиме. Каждое приложение выполняет вывод в отведенную ему прямоугольную область экрана, называемую окном. Окно состоит из стандартных элементов. Это упрощает освоение программ пользователем и облегчает работу программиста, поскольку в его распоряжение предоставляются библиотеки интерфейсных компонентов. Такие библиотеки входят в состав систем программирования.
Интерфейсные компоненты обращаются к аппаратуре не непосредственно, а через функции операционной системы, называемые API (Application Program Interface — программный интерфейс приложения). API-функции находятся в динамических библиотеках (.Dynamic Link Library, DLL), разделяемых всеми приложениями. Эти библиотеки называются динамическими потому, что находящиеся в них функции не подключаются к каждому исполняемому файлу до выполнения программы, а вызываются в момент обращения к ним.
В основе пользовательского интерфейса Windows лежит представление экрана как рабочего стола, на котором располагаются «листы бумаги» — окна приложений, ярлыки и меню. Одни элементы могут полностью или частично перекрывать другие, пользователь может изменять их размеры и перемещать их. Приложение может иметь несколько окон, одно из них является главным. При закрытии главного окна приложение завершается.
¨ Поддержка виртуального адресного пространства для каждого приложения. Каждому приложению доступно пространство адресов оперативной памяти размером до 4 Гбайт1. Операционная система отображает его на физические адреса и обеспечивает защиту приложений друг от друга. В разных версиях Windows защита выполняется с различной степенью надежности, например, системы Windows 95/98 гораздо менее надежны, чем Windows NT/2000.
¨ Возможность обмена данными между приложениями. Приложения могут обмениваться данными через буфер обмена или используя другие механизмы, например OLE (Object Linking and Embedding — связывание и внедрение объектов) или именованные программные каналы.
¨ Возможность запуска старых программ. В 32-разрядных версиях Windows можно выполнять 16-разрядные Windows-программы, а также программы, написанные под MS-DOS. Последние запускаются в так называемой виртуальной DOS-машине, которая создает у программы полное «впечатление» того, что она выполняется под управлением MS-DOS в монопольном режиме.
¨ Принцип событийного управления (рассматривается в следующем разделе).
Событийно-управляемое программирование
В основу Windows положен принцип событийного управления. Это значит, что и сама система, и приложения после запуска ожидают действий пользователя и реагируют на них заранее заданным образом. Любое действие пользователя (нажатие клавиши на клавиатуре, щелчок кнопкой мыши, перемещение мыши) называется событием. Структура программы, управляемой событиями, изображена на рис. 14.1.
Событие воспринимается Windows и преобразуется в сообщение — запись, содержащую необходимую информацию о событии (например, какая клавиша была нажата, в каком месте экрана произошел щелчок мышью). Сообщения могут поступать не только от пользователя, но и от самой системы, а также от активного или других приложений. Определен достаточно широкий круг стандартных сообщений, образующий иерархию, кроме того, можно определять собственные сообщения.
Сообщения поступают в общую очередь, откуда распределяются по очередям приложений. Каждое приложение содержит цикл обработки сообщений, который выбирает сообщение из очереди и через операционную систему вызывает подпрограмму, предназначенную для его обработки (рис. 14.2). Таким образом, Windows-приложение состоит из главной программы, обеспечивающей инициализацию и завершение приложения, цикла обработки сообщений и набора обработчиков событий.
Среда Visual Studio.NET содержит удобные средства разработки Windows-приложений, выполняющие вместо программиста рутинную работу — создание шаблонов приложения и форм, заготовок обработчиков событий, организацию цикла обработки сообщений и т. д. Рассмотрим эти средства.
Шаблон Windows-приложения
Создадим новый проект (File ► New ► Project), выбрав шаблон Windows Application (рис. 14.3). После более длительных раздумий, чем для консольного приложения, среда сформирует шаблон Windows-приложения. Первое отличие, которое бросается в глаза, — вкладка заготовки формы Form1.cs[Design], расположенная в основной части экрана. Форма представляет собой окно и предназначена для размещения компонентов (элементов управления) — меню, текста, кнопок, списков, изображений и т. д.
Среда создает не только заготовку формы, но и шаблон текста приложения. Перейти к нему можно, щелкнув в окне Solution Explorer (View ► Solution Explorer) правой кнопкой мыши на файле Formi .cs и выбрав в контекстном меню команду View Code. При этом откроется вкладка с кодом формы, который, за исключением комментариев, приведен в листинге 14.1. Представлять себе, что написано в вашей программе, весьма полезно, поэтому давайте внимательно рассмотрим этот текст.
Листинг 14.1. Шаблон Windows-приложения
using System;
using System.Drawing;
using System.Col lections;
using System.ComponentModel;
using System.Windows.Forms;,
using System.Data;
namespace WindowsApplicationl
{
public class Forml : System.Windows.Forms.Form
{ .
private System.ComponentModel.Container components = null;
public Forml()
Initial izeComponentO;
}
protected override void DisposeC bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Di spose();
}
}
base.DisposeC disposing );
}
#region Windows Form Designer generated code
private void Initial izeComponent( )
{
this.components = new System.ComponentModel .Container( );
this.Size = new System.Drawing.Size(300,300);
this.Text = "Forml";
}
#endregion
static void Main( )
{
Application. Run (new Forml( ));
}
}
}
Приложение начинается с директив использования пространств имен библиотеки .NET. Для пустой формы, не содержащей ни одного компонента, необходимыми являются только две директивы:
using System;
using System.Windows.Forms;
Остальные директивы добавлены средой «на вырост». С пространством имен System вы уже знакомы. Пространство System.Windows.Forms содержит огромное количество типов, являющихся строительными блоками Windows-приложений.
Список наиболее употребительных элементов этого пространства имен приведен в табл. 14.1, а часть иерархии — на рис. 14.4.
Мы будем изучать типы пространства имен Windows. Forms по мере необходимости.
Продолжим рассмотрение листинга 14.1. В нем описан класс Forml, производный от класса Form. Он наследует от своего предка множество элементов, которые мы рассмотрим в следующих разделах. В самом классе Forml описано новое закрытое поле components — контейнер для хранения компонентов, которые можно добавить в класс формы.
Конструктор формы вызывает закрытый метод InitializeComponent, автоматически формируемый средой (код метода скрыт между директивами препроцессора #region и #endregion). Этот метод обновляется средой при добавлении элементов управления на форму, а также при изменении свойств формы и содержащихся на ней элементов. Например, если изменить цвет фона формы с помощью окна свойств (Properties), в методе появится примерно такая строка:
this.BackColor = System.Drawing.SystemColors.AppWorkspace;
Метод освобождения ресурсов Dispose вызывается автоматически при закрытии формы. Точка входа в приложение, метод Main, содержит вызов статического метода Run класса Application. Метод Run запускает цикл обработки сообщений и выводит на экран форму, новый экземпляр которой передан ему в качестве параметра.
ПРИМЕЧАНИЕ_____________________________________________________________
Запуск приложения, для создания которого мы пока не написали ни строчки кода, можно выполнить с помощью команды меню Debug ► Start или клавиши F5. На экран выводится пустая форма, обладающая стандартной функциональностью окна Windows-приложения: например, она умеет изменять свои размеры и реагировать на действия с кнопками развертывания и закрытия.
Процесс создания Windows-приложения состоит из двух основных этапов:
1. Визуальное проектирование, то есть задание внешнего облика приложения.
2. Определение поведения приложения путем написания процедур обработки событий.
Визуальное проектирование заключается в помещении на форму компонентов (элементов управления) и задании их свойств и свойств самой формы с помощью окна свойств. Если его не видно, можно воспользоваться командой менюView ► Properties Window. Свойства отображаются либо в алфавитном порядке, либо сгруппированными по категориям. Способ отображения выбирается с помощью кнопок, расположенных в верхней части окна свойств (рис. 14.5).
Самый простой способ размещения компонента — двойной щелчок на соответствующем значке палитры компонентов Toolbox (если ее не видно, можно воспользоваться командой меню View ► Toolbox), при этом компонент помещается на форму. Затем компонент можно переместить и изменить его размеры с помощью мыши. Можно также сделать один щелчок на палитре и еще один щелчок в том месте формы, где планируется разместить компонент. В окне свойств отображаются свойства выбранного в данный момент компонента (он окружен рамкой).
Задание свойств выполняется либо выбором имеющихся в списке вариантов, либо вводом требуемого значения с клавиатуры. Если около имени свойства стоит значок +, это означает, что свойство содержит другие свойства. Они становятся доступными после щелчка на значке. Пример формы, на которой размещены компоненты с вкладки палитры Windows Forms, показан на рис. 14.6. Эти компоненты описаны в разделе «Элементы управления» (см. с. 325). Определение поведения программы начинается с принятия решений, какие действия должны выполняться при щелчке на кнопках, вводе текста, выборе пунктов меню и т. д., иными словами, по каким событиям будут выполняться действия, реализующие функциональность программы.
ВНИМАНИЕ___________________________________________________________________________
Интерфейс программы должен быть интуитивно понятным и по возможности простым. Часто повторяющиеся действия не должны требовать от пользователя выполнения сложных последовательностей операций.
________________________________________________________________________
Заготовка шаблона обработчика события формируется двойным щелчком на поле, расположенном справа от имени соответствующего события на вкладке Events окна свойств, при этом появляется вкладка окна редактора кода с заготовкой соответствующего обработчика (вкладка Events отображается в окне свойств при щелчке на кнопке с желтой молнией, как показано на рис. 14.5).
Для каждого класса определен свой набор событий, на которые он может реагировать. Наиболее часто используемые события:
¨ Activated — получение формой фокуса ввода;
¨ Click, Doubleclick — одинарный и двойной щелчки мышью;
¨ Closed — закрытие формы;
¨ Load — загрузка формы;
¨ KeyDown, KeyUp — нажатие и отпускание любой клавиши и их сочетаний;
¨ Keypress — нажатие клавиши, имеющей ASCII-код;
¨ MouseDown, MouseUp — нажатие и отпускание кнопки мыши;
¨ MouseMove — перемещение мыши;
¨ Paint — возникает при необходимости прорисовки формы.
В листинге 14.2 приведен текст программы, сформированной средой после размещения на форме компонентов, представленных на рис. 14.5, и выборе для кнопки события Click, а для поля ввода — события KeyPress. Из листинга для удобства восприятия удалены лишние элементы.
Листинг 14.2. Шаблон приложения с двумя компонентами и заготовками обработчиков событий
using System;
using System.Drawing;
using System.Col lections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace WindowsApplicationl
{
public class Forml : System.Windows.Forms.Form // 0
{
private System.Windows.Forms.TextBox textBoxl; // 1
private System.Windows.Forms.Button buttonl; // 2
private System.ComponentModel.Container components = null;
public Forml()
{
Initial izeComponent( );
}
protected override void Dispose( bool disposing ){...}
#region Windows Form Designer generated code
private void Initial izeComponent( )
{
this.textBoxl = new System.Windows.Forms.TextBox( ); // 3
this.buttonl = new System.Windows.Forms.Button( ); // 4
this.SuspendLayout( ); // 5
//
// textBoxl //
this.textBoxl.Location = new System.Drawing.Point(24, 16);
this.textBoxl.Name = "textBoxl";
this.textBoxl.Size = new System.Drawing.Size(240, 20);
this. textBoxl. Tablndex = 0;
this.textBoxl.Text = "textBoxl";
this.textBoxl.KeyPress += new //6
System.Windows.Forms.KeyPressEventHandler(this.textBoxl_KeyPress);
//
// buttonl
//
this.buttonl.Location = new System.Drawing.Point(192, 80);
this.buttonl.Name = "buttonl";
this.buttonl.Tablndex =1;
this.buttonl.Text = "buttonl";
this.buttonl.Click += new // 7
System.EventHandler(this.buttonl_Click); //
//
Forml
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 126);
this.Controls.AddCthis.buttonl); // 8
this.Controls.Add(this.textBoxl); // 9
this.Name = "Forml";
this.Text = "Forml";
this.ResumeLayout(false); // 10
#endregion
static void Main( )
Application. Run (new Forml( );
}
private void buttonl_Click(object sender. System.EventArgs e) // 11
{
private void textBoxl_KeyPress(object sender,
System.Wi ndows.Forms.KeyPressEventArgs e)
//12
Рассмотрим текст программы. В операторах 1 и 2 в класс формы добавляются два закрытых поля: строка ввода типа TextBox и кнопка типа Button. Эти типы определены в пространства имен System.Windows.Forms, которое подключается в соответствующей директиве using, поэтому можно записать эти и соседние строки проще:
public class Forml : Form //0
{
private TextBox textBoxl; //1
private Button buttonl:. //2
private Container components = null;
Чаще всего программист изменяет имена, присваиваемые элементам средой, более осмысленными. Это можно сделать, задав новое значение свойства Name элемента.
ПРИМЕЧАНИЕ_______________________________________________________________
Того же самого эффекта можно достичь и с помощью вкладки Class View (View ► Class View), выбрав в списке нужный элемент и изменив значение его свойства Name в окне свойств. Обратите внимание на то, что при этом в нем отображаются свойства кнопки buttonl не как элемента интерфейса, а как поля класса. Можно изменить имена и вручную, но это более трудоемко и чревато ошибками.
Самое важное происходит в методе InitializeComponent.
В операторах 3 и 4 создаются экземпляры компонентов, затем для каждого из них задаются свойства, определяющие их положение, размер, вид и т. д. Обратите внимание на операторы 6 и 1.\ В них регистрируются обработчики соответствующих событий. Механизм обработки событий тот же, что мы рассматривали в главе 10 (см. с. 232) — он описывается моделью «публикация — подписка».
Например, для кнопки buttonl, в составе которой есть событие Cl ick, регистрируется обработчик buttonl_Click, являющийся закрытым методом класса Forml. Это значит, что при наступлении события нажатия на кнопку (об этом сообщит операционная система) будет вызван соответствующий обработчик.
Имя обработчика формируется федой автоматически из имени экземпляра компонента и имени события. Обратите внимание на то, что обработчикам передаются два параметра: объект-источник события и запись, соответствующая типу события.
ПРИМЕЧАНИЕ_________________________________________________________
При задании обработчика можно задать и другое имя, для этого оно записывается справа от имени соответствующего события на вкладке Events окна свойств.
________________________________________________________________________
После создания экземпляров компонентов и настройки их свойств компоненты заносятся в коллекцию, доступ к которой выполняется через свойство Controls (операторы 8 и 9). Если этого не сделать, компоненты не будут отображаться на форме. Коллекция поддерживает методы добавления и удаления компонентов
(Add и Remove).
Таким образом, для размещения компонента на форме необходимо выполнить три действия:
1. Создать экземпляр соответствующего класса.
2. Настроить свойства экземпляра, в том числе зарегистрировать обработчик событий.
3. Поместить экземпляр в коллекцию компонентов формы.
Операторы 5 и 10 используются для того, чтобы все изменения в свойства элементов вносились одновременно. Для этого в операторе 5 внесение изменений «замораживается», а в операторе 10 разрешается.
В теле обработчиков событий (операторы 11 и 12) программист может наконец-то самостоятельно написать код, который будет выполняться при наступлении события. Добавим в эти строки операторы, выводящие окна сообщений с соответствующим текстом:
MessageBox.ShowC"Нажата кнопка buttonl"); //11
MessageBox.Show("Нажата клавиша " + e.KeyChar); // 12
Здесь используется простейший вариант статического метода Show класса MessageBox, определенного в пространстве имен System.Windows.Forms. Существуют более десяти перегруженных вариантов этого метода, позволяющих варьировать вид выводимой информации, например задать заголовок окна и наименования отображаемых на нем кнопок.
Прежде чем приступить к изучению форм и элементов управления, размещаемых на формах, необходимо рассмотреть их общего предка — класс Control.
Класс Control
Класс Control является базовым для всех отображаемых элементов, то есть элементов, которые составляют графический интерфейс пользователя, например кнопок, списков, полей ввода и форм. Класс Control реализует базовую функциональность интерфейсных элементов. Он содержит методы обработки ввода пользователя с помощью мыши и клавиатуры, определяет размер, положение, цвет фона и другие характеристики элемента. Для каждого объекта можно определить родительский класс, задав свойство Parent, при этом объект будет иметь, например, такой же цвет фона, как и его родитель.
Наиболее важные свойства класса Control перечислены в табл. 14.2, методы — в табл. 14.3.
Перечисление Control Styles задает возможные значения стиля формы в виде битовых флагов, поэтому можно использовать их комбинации. Значения всех констант перечисления можно посмотреть в электронной документации, а для первого знакомства достаточно одного — ResizeRedraw. Этот стиль определяет, что при изменении размеров формы она будет автоматически перерисована. По умолчанию перерисовка не выполняется, и если на форме есть какое-либо изображение, результат изменения размеров формы может сильно озадачить. В табл. 14.4 перечислена небольшая часть событий, определенных в классе Control.
Применение наиболее важных элементов, описанных в таблицах, рассматривается в следующих разделах.
При написании приложений применяются два способа обработки событий:
¨ замещение стандартного обработчика;
¨ задание собственного обработчика.
В большинстве случаев применяется второй способ. Среда разработки создает заготовку обработчика по двойному щелчку на поле, расположенном справа от имени соответствующего события на вкладке Events окна свойств. При этом в код приложения автоматически добавляется строка, регистрирующая этот обработчик.
Первый способ, то есть переопределение виртуальных методов OnXXXX (OnMouseMove, OnKeyDown, OnResize, OnPaint и т. п.), применяется в основном тогда, когда перед реакцией на событие требуется выполнить какие-либо дополнительные действия. За подробностями интересующиеся могут обратиться к [27].
Элементы управления, или компоненты, помещают на форму с помощью панели инструментов ToolBox (View ► ToolBox). В этом разделе кратко описаны простейшие компоненты и приведены примеры их использования.
Обычно компоненты применяют в диалоговых окнах для получения данных от пользователя или его информирования.
Метка Label
Метка предназначена для размещения текста на форме. Текст хранится в свойстве Text. Можно задавать шрифт (свойство Font), цвет фона (BackColor), цвет шрифта (ForeColor) и выравнивание (TextAlign) текста метки. Метка может автоматически изменять размер в зависимости от длины текста (AutoSize = True). Можно разместить на метке изображение (Image) и задать прозрачность (установить для свойства BackColor значение Color.Transparent).
Метка не может получить фокус ввода, следовательно, не обрабатывает сообщения от клавиатуры. Пример использования меток приведен далее (см. с. 327).
Кнопка Button
Основное событие, обрабатываемое кнопкой, — щелчок мышью (Click). Кроме того, кнопка может реагировать на множество других событий — нажатие клавиш на клавиатуре и мыши, изменение параметров и т. д. Нажатие клавиши Enter или пробела, если при этом кнопка имеет фокус ввода, эквивалентно щелчку мышью на кнопке.
Можно изменить начертание и размер шрифта текста кнопки, который хранится в свойстве Text, задать цвет фона и фоновое изображение так же, как и для метки.
Если занести имя кнопки в свойство AcceptButton формы, на которой расположена кнопка, то нажатие клавиши Enter вызывает событие Click, даже если кнопка не имеет фокуса ввода. Такая кнопка имеет дополнительную рамку и называется кнопкой по умолчанию.
Аналогично, если занести имя кнопки в свойство Cancel Button формы, на которой расположена кнопка, то нажатие клавиши Esc вызывает событие Cl i ck для этой кнопки.
Кнопки часто используются в диалоговых окнах. Как видно из названия, такое окно предназначено для диалога с пользователем и запрашивает у него какие-либо сведения (например, какой выбрать режим или какегй файл открыть). Диалоговое окно обладает свойством модальности. Это означает, что дальнейшие действия с приложением невозможны до того момента, пока это окно не будет закрыто. Закрыть окно можно, либо подтвердив введенную в него информацию щелчком на кнопке ОК (или Yes), либо отменив ее с помощью кнопки закрытия окна или, например, кнопки Cancel.
Для сохранения информации о том, как было закрыто окно, у кнопки определяют свойство DialogResult. Это свойство может принимать стандартные значения из перечисления DialogResult, определенного в пространстве имен System.Windows.Forms. Значения перечисления приведены в табл. 14.5. Пример использования кнопок приведен в следующем разделе.
Поле ввода TextBox
Компонент TextBox позволяет пользователю вводить и редактировать текст, который запоминается в свойстве Text. Можно вводить строки практически неограниченной длины (приблизительно до 32 000 символов), корректировать их, а также вводить защищенный текст (пароль) путем установки маски, отображаемой вместо вводимых символов (свойство PasswordChar). Для обеспечения возможности ввода нескольких строк устанавливают свойства Multiline, Scrol I Bars и Wordwrap. Доступ только для чтения устанавливается с помощью свойства Readonly.
Элемент содержит методы очистки (Clear), выделения (Select), копирования в буфер (Сору), вставки из него (Paste) и др., а также реагирует на множество событий, основными из которых являются KeyPress и KeyDown.
Рассмотрим создание простого приложения, в котором использованы компоненты типа Label, Button и TextBox. Главное окно приложения показано на рис. 14.7.
Пользователь вводит число и нажимает клавишу Enter, после чего введенное значение сравнивается с числом, «задуманным» генератором случайных чисел. Если пользователь не угадал, выводится сообщение «Не угадали!». Каждое следующее сообщение для наглядности немного сдвигается вправо относительно предыдущего. После совпадения выводится итоговое сообщение, и фокус ввода передается на кнопку Еще раз (рис. 14.8). «Коэффициент невезучести» определяется как количество попыток, деленное на максимально возможное значение числа.
Создание приложения начинается с проектирования его интерфейса. На форме располагаются метки, поля ввода и кнопка. В окне свойств их свойству Text задаются перечисленные в табл. 14.6 значения (в окне отображаются свойства выделенного элемента; элемент выделяется щелчком мыши).
Поведение приложения задается в обработчиках событий. Число надо загадывать до того, как пользователь введет первый ответ, а затем после каждого щелчка на кнопке Еще раз. Следовательно, эти действия должны выполняться при загрузке формы (событие Load) и щелчке на кнопке (событие Click). Ответ пользователя анализируется при нажатии клавиши Enter после ввода числа (событие Key Press элемента textBoxl).
Вызов редактора программ выполняется двойным щелчком рядом с соответствующим событием элемента на вкладке Events окна свойств (см. рис. 14.5). Для хранения загаданного числа и ответа пользователя применяются поля i и к. В константе шах хранится максимально возможное число. Переменная rnd представляет собой экземпляр генератора случайных чисел2.
Сокращенный текст программы с комментариями приведен в листинге 14.3.
Листинг 14.3. Программа «Угадай число»
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace WindowsApplicationl
public class Forml : Form
private Label label 1:
private TextBox textBoxl;
private Button buttonl;
private Label label2;
private Label Iabel3:
private Container components = null;
public FormH) { ... }
protected override void Dispose( boo! disposing ){...}
Windows Form Designer generated code { ... }
const int max = 10; // максимальное значение загадываемого числа
Random rnd; // экземпляр генератора случайных чисел
Int I,k; // загадка и отгадка
static void Main( ) { ... }
private void Forml_Load(object sender, EventArgs e)
rnd = new Random( );
i = rnd.Next(max); // загадано число в диапазоне от 0 до max
}
private void textBoxl_KeyPress(object sender. KeyPressEventArgs e)
{
int n;
if ( e.KeyChar != (char)13 )
return; // если нажата не клавиша Enter, выйти
try // преобразование ввода пользователя в число
n = Convert.ToInt32(textBoxl.Text);
} catch
n = -1- // если введено не число, принять за неверный ответ
}
If ( n ! = I ) // ============ пользователь не угадал
label2.Left += 5;
label2.Text = "Не угадали!";
textBoxl. Clear( );
k++. // увеличение счетчика попыток
buttonl.Visible = false;
else // ============== пользователь угадал
{
1abe!2.Left = 32; // восстановить начальное положение метки
label2.Text = "Коэффициент невезучести";
double koef = 1.0 * k / max;
label3.Text = koef .ToString( );
buttonl.Visible =true;
private void buttonl_Click(object sender, System.EventArgs e)
{
i = rnd.Next(max); // загадано число в диапазоне от 0 до max
к = 0; // обнуление количества попыток ответа
textBoxl.Clear( ); // поле ввода очищено
textBoxl.Focus( ): // курсор установлен на поле ввода
1abel2.Text = ""; // метки очищены
labels.Text = "";
Меню MainMenu и ContextMenu
Главное меню MainMenu размещается на форме таким же образом, как и другие компоненты: двойным щелчком на его значке на панели Toolbox. При этом значок располагается под заготовкой формы, а среда переходит в режим редактирования пунктов меню. Каждый пункт меню представляет собой объект типа Menultem, и при вводе пункта меню мы задаем его свойство Text. Переход к заданию заголовка следующего пункта меню выполняется либо щелчком мыши, либо нажатием клавиши Enter и клавиш со стрелками. Обычно, чтобы сделать программу понятнее, изменяют также свойства Name каждого пункта так, чтобы они соответствовали названиям пунктов.
Пункт меню может быть запрещен или разрешен (свойство Enabled), видим или невидим (Visible), отмечен или не отмечен (Checked). Заготовка обработчика событий формируется двойным щелчком на пункте меню.
Любое приложение обычно содержит в меню команду Exi t, при выборе которой приложение завершается. Для закрытия приложения можно воспользоваться либо методом Close класса главной формы приложения, либо методом Exit класса Application, например:
private void Exit_Click(object sender, EventArgs e)
{ // имя пункта меню - Exit
Close( ):
// или:
// Application.Exit( );
}
Контекстное меню — это меню, которые вызывается во время выполнения программы по нажатию правой кнопки мыши на форме или элементе управления. Обычно в этом меню размещаются пункты, дублирующие пункты главного меню, или пункты, определяющие специфические для данного компонента действия.
Контекстное меню ContextMenu создается и используется аналогично главному (значок контекстного меню появляется на панели инструментов, если воспользоваться кнопкой прокрутки). Для привязки контекстного меню к компоненту следует установить значение свойства ContextMenu этого компонента равным имени контекстного меню.
Флажок CheckBox
Флажок используется для включения-выключения пользователем какого-либо режима. Для проверки, установлен ли флажок, анализируют его свойство Checked, принимающее значение true или false. Флажок может иметь и третье состояние — «установлен, но не полностью». Как правило, это происходит в тех случаях, когда устанавливаемый режим определяется несколькими «подрежимами», часть из которых включена, а часть выключена. В этом случае используют свойство CheckState, которое может принимать значения Checked, Unchecked и Intermediate.
Кроме того, флажок обладает свойством ThreeState, которое управляет возможностью установки третьего состояния пользователем с помощью мыши. Для флажка можно задать цвет фона и фоновое изображение так же, как и для метки. Свойство Appearance управляет отображением флажка: либо в виде собственно флажка (Normal), либо в виде кнопки (Button), которая «залипает» при щелчке на ней мышью.
Флажки используются в диалоговых окнах как поодиночке, так и в группе, причем все флажки устанавливаются независимо друг от друга. Пример приложения с флажками приведен далее в этой главе.
Переключатель RadioButton
Переключатель позволяет пользователю выбрать один из нескольких предложенных вариантов, поэтому переключатели обычно объединяют в группы. Если один из них устанавливается (свойство Checked), остальные автоматически сбрасываются. Программист может менять стиль и цвет текста, связанного с переключателем, и его выравнивание. Для переключателя можно задать цвет фона и фоновое изображение так же, как и для метки.
Переключатели можно поместить непосредственно на форму, в этом случае все они составят одну группу. Если на форме требуется отобразить несколько групп переключателей, их размещают внутри компонента Group или Panel.
Свойство Appearance управляет отображением переключателя: либо в традиционном виде (Normal), либо в виде кнопки (Button), которая «залипает» при щелчке на ней мышью.
Пример использования переключателей приведен далее в этой главе.
Панель GroupBox
Панель GroupBox служит для группировки элементов на форме, например для того, чтобы дать общее название и визуально выделить несколько переключателей или флажков, обеспечивающих выбор связанных между собой режимов.
Приведенная в листинге 14.4 программа запрашивает у пользователя, массив какой длины он хочет создать, и создает целочисленный массив с помощью генератора случайных чисел. Пользователь может выбрать диапазон значений элементов: либо [—10; 10], либо [—100; 100]. После создания массива можно вычислить его максимальный элемент и/или количество положительных элементов. Окно приложения показано на рис. 14.9. Для удобства имена (свойство Name) большинства компонентов изменены, как указано на рисунке. Переключатели размещены на панели типа GroupBox. Установлено свойство Checked компонента radioButtonl, очищены все поля ввода, а для полей maxtextBox и numPosittextBox установлено свойство Readonly (только для чтения).
ПРИМЕЧАНИЕ _______________________________________________________
При визуальном проектировании важно размещать компоненты приятным глазу образом. При этом удобно пользоваться командами меню Format, предварительно выделив нужные компоненты, «обводя» их мышью или выделяя мышью при нажатой клавише Shift или Ctrl. Например, можно выровнять компоненты по левому краю, выделив их и воспользовавшись командой меню Format ► Align ► Lefts.
Все действия в программе выполняются щелчками на двух кнопках, то есть обрабатываются два события Click.
Листинг 14.4. Вычисления в массиве
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data:
namespace WindowsApplicationl
{
public class Forml : Form
{
private Label labell;
private GroupBox groupBoxl;
private RadioButton radioButtonl;
private RadioButton radioButton2;
private TextBox numtextBox;
private CheckBox numPositcheckBox;
private Button createbutton;
private Button calcbutton;
private TextBox maxtextBox;
private TextBox numPosittextBox;
private TextBox arraytextBox;
private CheckBox maxcheckBox;
private Container components = null;
public Forml( ) { ... }
protected override void Dispose( bool disposing ){...}
Windows Form Designer generated code { ... }
int[] arr; // описание массива
static void Main( ) { ... }
private void createbutton_Click(object sender, EventArgs e)
{
Random rnd = new Random( );
int a = -10. b = 10; // диапазон значений элементов
if ( radioButton2.Checked )
{
a = -100; b = 100; // корректировка диапазона
}
int n = 0; try
{
n = int.Parse(numtextBox.Text); // длина массива
} , catch
{
MessageBox.Show("Введите количество элементов!");
numtextBox.Clear( );
numtextBox. Focus( );
arraytextBox.Clear( ); /,/ очистка полей ввода
maxtextBox.Clear( );
numPosittextBox.-Clear( );
if ( п < 0 ) п = -п; // если введено отрицательное число
агг = new int[n]; // создание массива
for ( int i =0; i * n; ++i )
{
arr[i] = rnd.Next(a, b); // задание элемента массива
arraytextBox.Text += " " + arr[i]; // вывод массива
private void calcbutton_Click(object sender, EventArgs e)
{
int max = arr[0];
int numPosit = 0;
for ( int i = 0; i < агг.Length; ++i )
{
if ( arr[i] > max ) max = arr[i]; // поиск максимума
if ( arr[i] > 0 ) ++numPosit; // количество положительных
}
if ( maxcheckBox.Checked )
maxtextBox.Text - max.ToString( );
else maxtextBox.Text = "";
if ( numPositcheckBox.Checked )
numPosittextBox.Text = numPosit.ToString( );
else numPosittextBox.Text = "";
Список ListBox
Список служит для представления перечней элементов, в которых пользователь может выбрать одно (свойство Select!onMode равно One) или несколько значений (свойство SelectionMode равно MultiSimple или MultiExtended). Если значение свойства SelectionMode равно MultiSimple, щелчок мышью на элементе выделяет его или снимает выделение. Значение Multi Extended позволяет использовать при выделении диапазона строк клавишу Shift, а при добавлении элемента — клавишу Ctrl, аналогично проводнику Windows. Запретить выделение можно, установив значение свойства SelectionMode, равное None.
Чаще всего используются списки строк, но можно выводить и произвольные изображения. Список может состоять из нескольких столбцов (свойство Multi Column) и быть отсортированным в алфавитном порядке (Sorted = True).
Элементы списка нумеруются с нуля. Они хранятся в свойстве Items, представляющем собой коллекцию. В Items можно добавлять элементы с помощью методов
Add, AddRange и Insert. Для удаления элементов служат методы Remove и RemoveAt, удаляющие заданный элемент и элемент по заданному индексу соответственно.
Выделенные элементы можно получить с помощью свойств Selected Items и Selected-Indices, предоставляющих доступ к коллекциям выделенных элементов и их индексов.
В листинге 14.5 приведен пример приложения, которое отображает в списке типа ListB§x строки, считанные из входного файла, а затем по щелчку на кнопке Запись выводит выделенные пользователем строки в выходной файл. Вид окна приложения приведен на рис. 14.10.
Листинг 14.5. Работа со списком строк
using System;
using System.10;
using System.Drawing;
using System.Collections;
using System.ComponentHodel;
using System.Windows.Forms;
using System.Data;
using System.Collections.Specialized;
namespace WindowsApplicationl
{
public class Forml : Form
{
private ListBox listBoxl;
private Button buttonl;
private Container components = null;
public Forml( ) { ... }
protected override void Dispose( bool disposing ){
Windows Form Designer generated code { ... }
static void Main( ) { ... }
private void Forml_Load(object sender, EventArgs e)
{
try
{
StreamReader f | new StreamReader( "input.txt" );
string buf;
while ( ( buf = f.ReadLine( ) ) != null ) // чтение из файла
listBoxl.Items.Add(buf); // занесение в список
}
catch ( FileNotFoundException exc )
{
MessageBox.Show( exc.Message );
return;
}
private void buttonl_Click(object sender, EventArgs e)
{
StreamWriter f = new StreamWriter( "output.txt" );
foreach ( string item in listBoxl.Selectedltems )
f.WriteLine(item); // запись в файл
f.Close( );
}
}
На панели инструментов расположено множество компонентов, не рассмотренных в этой книге. Для их изучения следует обратиться к справочной системе .NET. Она организована понятно и единообразно, и даже те, кто не изучал английский язык, смогут при некотором навыке извлечь оттуда необходимую информацию.
СОВЕТ_________________________________________________________________
При изучении компонента рекомендуется следующая последовательность действий. Разместите компонент на форме, выделите его, нажмите клавишу F1 и перейдите по ссылке ...overview (обзор). Изучите разделы Remarks и Example, затем перейдите по ссылке ...Members (элементы класса), расположенной в верхней части окна. Попытайтесь получить представление о возможностях изучаемого класса, выделив главные из его свойств и открытых методов. После этого можно вернуться к заготовке приложения и начать экспериментировать со свойствами, а затем — с методами класса.
В следующей части этой главы будут коротко описаны основные элементы классов Form и Application, входящих в состав любого приложения.
Предварительные замечания о формах
Класс Form наследует от длинной цепочки своих предков множество элементов, определяющих вид и поведение окон различного типа. Генеалогическое древо класса Form выглядит так: Object→ MarshalByRefObject→ Component →Control→
Scrol1ableControl→ Contai nerControl.
Окна приложения могут иметь различные вид и назначение. Все окна можно разделить на модальные и немодальные. Модальное окно не позволяет пользователю переключаться на другие окна того же приложения, пока не будет завершена работа с текущим окном. Как уже отмечалось, в виде модальных обычно оформляют диалоговые окна, требующие от пользователя ввода какой-либо информации. Модальное окно можно закрыть щелчком на кнопке наподобие ОК, подтверждающей введенную информацию, на кнопке закрытия окна или на кнопке вроде Cancel, отменяющей ввод пользователя. Примером модального окна может служить окно сообщений MessageBox, упоминавшееся в разделе «Шаблон Windows-приложения» (см. с. 322).
Немодальное окно позволяет переключаться на другие окна того же приложения. Немодальные окна являются, как правило, информационными. Они используются в тех случаях, когда пользователю желательно предоставить свободу выбора — оставлять на экране какую-либо информацию или нет.
Каждое приложение содержит одно главное окно. Класс главного окна приложения содержит точку входа в приложение (статический метод Main). При закрытии главного окна приложение завершается.
В случае использования многодокументного интерфейса (Multiple Document Interface, MDI) одно родительское окно может содержать другие окна, называемые дочерними. При закрытии родительского окна дочерние окна закрываются автоматически. Вид окна определяет его функциональность, например, окно с одинарной рамкой не может изменять свои размеры.
Рассмотрим наиболее интересных предков класса формы. Их элементы наследуются не только формой, но и другими компонентами, такими как поля ввода или кнопки.
Класс Marshal ByRefObject наделяет своих потомков некой особенностью, благодаря которой обращение к ним выполняется по ссылке, то есть локальная копия объекта не создается.
Класс Component обеспечивает потомков способностью взаимодействовать с контейнером, в котором они расположены. Кроме того, в нем определен метод Dispose, который автоматически вызывается, когда экземпляр класса более не используется. Поэтому для освобождения ресурсов, связанных с приложением, обычно переопределяют этот метод.
Класс Control, являющийся предком всех интерфейсных элементов, рассмотрен в этой главе ранее. В классе Scrol lableControl определены элементы, позволяющие компоненту иметь горизонтальную и вертикальную полосы прокрутки. Свойства AutoScrol 1 и AutoScrol IMinSize обеспечивают автоматическое появление полос прокрутки в тех случаях, когда выводимая информация не помещается в компоненте.
Класс ContainerControl обеспечивает своих потомков возможностью управлять размещенными внутри них дочерними компонентами. Например, на форме обычно располагаются несколько кнопок, меток и т. п., а на панели — несколько флажков или переключателей. Свойства и методы класса позволяют установить фокус ввода на элемент или получать информацию о том, какой элемент имеет фокус ввода, а также управлять порядком получения фокуса с помощью свойств TabStop и Tablndex.
Класс Form
Класс Form представляет собой заготовку формы, от которой наследуются классы форм приложения. Помимо множества унаследованных элементов, в этом классе определено большое количество собственных элементов, наиболее употребительные из которых приведены в табл. 14.7-14.9.
Об использовании некоторых элементов, перечисленных в таблицах, рассказывается в следующем разделе.
В библиотеке .NET нет специального класса для представления диалоговых окон. Вместо этого устанавливаются определенные значения свойств в классе обычной формы. В диалоговом окне можно располагать те же элементы управления, что и на обычной форме. Диалоговое окно характеризуется:
¨ неизменяемыми размерами (FormBorderStyle = FixedDialog);
¨ отсутствием кнопок восстановления и свертывания в правом верхнем углу заголовка формы (MaximizeBox = False, MinimizedBox = False);
¨ наличием кнопок наподобие ОК, подтверждающей введенную информацию, и Cancel, отменяющей ввод пользователя, при нажатии которых окно закрывается (AcceptButton = имя__кнопки_ОК, Cancel Button = имя_кнопки_Сапсе1);
¨ установленным значением свойства DialogResult для кнопок, при нажатии которых окно закрывается.
Для отображения диалогового окна используется метод ShowModal, который формирует результат выполнения из значений перечисления DialogResult, описанных в табл. 14.5. Если пользователь закрыл диалоговое окно щелчком на кнопке наподобие ОК, введенную им информацию можно использовать в дальнейшей работе. Закрытие окна щелчком на кнопке вроде Cancel отменяет все введенные данные. Диалоговое окно обычно появляется при выборе пользователем некоторой команды меню на главной форме.
В листинге 14.6 приведен пример простого приложения, содержащего две формы. На главной форме (рис. 14.11) расположено меню из двух команд — Dialog и Exit. При выборе команды Dialog появляется диалоговое окно, включающее метку Введите информацию, поле ввода текста и две кнопки, ОК и Cancel (рис. 14.12).
Если пользователь введет текст в диалоговое окно и закроет его щелчком на кнопке ОК, этот текст отобразится в поле метки в главном окне. Если диалоговое окно будет закрыто щелчком на кнопке Cancel, поле метки останется неизменным.
Добавление в проект второй формы выполняется выбором в меню команды Project ► Add Windows Form. Начнем визуальное проектирование с этой формы. Значения свойств компонентов, установленных с помощью окна свойств, перечислены в табл. 14.10.
Пользователь вводит информацию в поле ввода textBoxl, которое является закрытым элементом формы. Чтобы получить значение свойства Text поля ввода, добавим в описание класса свойство Info. Это единственное дополнение, которое вносится в шаблон текста класса формы:
public class Form2 : Form
{
private Label label 1;
private TextBox textBoxl;
private Button btnOK;
private Button btnCancel;
private Container components = null;
public string Info
{
get
{
return textBoxl.Text;
Визуальное проектирование главной формы еще проще, чем первой, и сводится к размещению главного меню и метки. Для наглядности метка окружена рамкой (Borderstyle = FixedSingle). Вот как выглядят обработчики событий для пунктов меню:
private void menuIteml_Click( object sender, EventArgs e )
{
Form2 f = new Form2( ); // создание экземпляра класса окна
if ( f.ShowDialogO == DialogResult.OK ) // отображение окна
labell.Text = f.Info;
}
private void menuItem2_Click( object sender, EventArgs e )
{
Close( ); // закрытие главного окна
}
Как видите, для отображения диалогового окна следует создать экземпляр объекта соответствующей формы, а затем вызвать для этого объекта метод ShowDialog.
При подтверждении ввода текст пользователя можно получить с помощью свойства Info, доступного только для чтения. При необходимости передавать информацию не только из диалогового окна, но и в него, можно добавить в описание свойства часть set.
ПРИМЕЧАНИЕ_______________________________________________________________
Следует различать процесс создания формы — объекта класса Form или его наследника — от процесса вывода формы на экран. Форма, как и любой объект, создается при выполнении операции new с вызовом конструктора. Для вывода формы служит метод Show или ShowDialog класса Form, вызываемый соответствующим объектом. Для скрытия формы используется метод Hide. Фактически, методы Show и Hide изменяют свойство Visible объекта. Разница между скрытием формы методом Hi de и ее закрытием методом Cl ose состоит в том, что первый из них делает форму невидимой, но не изменяет сам объект, а метод Cl ose делает объект недоступным и закрывает все его ресурсы.
Класс Application
Класс Application, описанный в пространстве имен System.Windows.Forms, содержит статические свойства, методы и события, предназначенные для управления приложением в целом и получения его общих характеристик. Наиболее важные элементы класса Application перечислены в табл. 14.11.
Многие свойства класса Applicati on позволяют получить метаданные сборки (например, номер версии или имя компании), не используя типы пространства имен System.Reflection. Программист не часто работает непосредственно с классом Application, поскольку большую часть необходимого кода среда формирует автоматически.
Для вывода линий, геометрических фигур, текста и изображений необходимо создать экземпляр класса Graphics, описанного в пространстве имен System.Drawing. Существуют различные способы создания этого объекта.
Первый способ состоит в том, что ссылку на объект Graphics получают из параметра PaintEventArgs, передаваемого % обработчик события Paint, возникающего при необходимости прорисовки формы или элемента управления:
private void Forml_Paint( object sender, PaintEventArgs e )
{ Graphics g = e.Graphics;
// использование объекта
}
Второй способ — использование метода CreateGraphics, описанного в классах формы и элемента управления:
Graphics g;
g = this.CreateGraphics( );
Третий способ — создание объекта с помощью объекта-потомка Image. Этот способ используется для изменения существующего изображения:
Bitmap bm = new BitmapC "d:\\picture.bmp" );
Graphics g = Graphics.Fromlmage( bm );.
После создания объекта типа Graphics его можно применять для вывода линий, геометрических фигур, текста и изображений. Основными объектами, которые при этом используются, являются объекты классов:
¨ Реп — рисование линий и контуров геометрических фигур;
¨ Brush — заполнение областей;
¨ Font — вывод текста;
¨ Color — цвет.
В листинге 14.6 представлен код приложения, в котором на форму выводятся линия, эллипс и текст. Вид формы приведен на рис. 14.13.
Листинг 14.6. Работа с графическими объектами
using System:
using System.Drawing:
using System.Windows.Forms;
namespace WindowsApplicationl
{
public partial class Forml : Form
{
public Forml( ) { InitializeComponent( ); }
private void Forml_Paint( object sender, PaintEventArgs e )
{
using ( Graphics g = e.Graphics ) //1
using ( Pen pen = new Pen( Color.Red ) ) //2 {
g.DrawLine( pen, 0, 0, 200, 100 );
g.DrawEllipse( pen, new Rectangle(50, 50, 100, 150) );
}
string s = "Sample Text";
Font font = new Font( "Arial", 18 ); //3
SolidBrush brush = new SolidBrush( Color.Black ); //4
float x = 100.OF;
float у = 20.OF;
g.DrawString( s, font, brush, x, у );
font.Dispose( ); // 5
brush.Dispose( ); //6
Графические объекты потребляют системные ресурсы, поэтому рекомендуется вызывать для них метод освобождения ресурсов Dispose. Для упрощения работы с такими объектами в С# есть оператор using со следующим синтаксисом:
using ( выделение_ресурса ) оператор
Под ресурсом здесь понимается объект, который реализует интерфейс System. IDisposable, включающий метод Dispose. Код, использующий ресурс, вызовом этого метода сигнализирует о том, что ресурс больше не требуется. Если метод Di spose не был вызван, освобождение ресурса выполняется в процессе сборки мусора.
Оператор using неявным образом вызывает метод Dispose в случае успешного создания и использования объекта. Этот способ применен в операторах 1 и 2.
В операторах 3 и 4 объекты создаются обычным образом, поэтому для них требуется явный вызов Dispose, что и происходит в операторах 5 и 6.
Как видно даже из этого простого листинга, для вывода графики требуется кропотливое изучение множества свойств и методов множества стандартных классов, описание которых, во-первых, очень объемное, во-вторых, невыносимо скучное, а в-третьих, не входит в задачу учебника по основам программирования.
Рекомендации по программированию
Процесс создания Windows-приложения состоит из двух основных этапов, которые могут чередоваться между собой: это визуальное проектирование приложения и определение его поведения.
При задании внешнего облика приложения следует обратить внимание на стандарты интерфейса Windows-приложений: компания Microsoft, в свое время заимствовавшая идею стандартного графического интерфейса у компании Apple, довела эту идею до совершенства, детально регламентировав вид окон, расположение, цветовую гамму и пропорции компонентов.
Основная сложность для начинающих заключается в разработке алгоритма: по каким событиям будут выполняться действия, реализующие функциональность программы, какие действия должны выполняться при щелчке на кнопках, вводе текста, выборе пунктов меню и т. д.
Интерфейс программы должен быть интуитивно понятным и по возможности простым. Часто повторяющиеся действия не должны требовать от пользователя выполнения сложных последовательностей операций. Команды меню и компоненты, которые не имеет смысла использовать в данный момент, рекомендуется делать неактивными. Вопросы, задаваемые пользователю программы, должны быть ненавязчивыми («Нет, а все-таки Вы действительно хотите удалить этот файл?») и немногословными, но при этом не допускать двояких толкований.
Эта глава получилась самой длинной из-за большого количества информации справочного характера. Несмотря на это приведенных сведений совершенно недостаточно для создания реальных Windows-приложений. К сожалению, мощь библиотеки .NET имеет оборотную сторону: для освоения необходимой информации требуется много времени и упорства, однако это единственный путь для тех, кто хочет заниматься программированием профессионально.
Конечно, пытаться запомнить все методы и свойства классов нет смысла, достаточно изучить состав используемых пространств имен, представлять себе возможности их элементов и знать, как быстро найти требуемую информацию.
Для дальнейшего изучения возможностей библиотеки можно рекомендовать документацию и дополнительную литературу [17], [18], [20], [31]. И последний совет: не следует считать себя программистом только на том основании, что вы умеете размещать компоненты на форме!
Дополнительные средства С#
В этой главе описаны дополнительные средства языка С# и среды Visual Studio: указатели, регулярные выражения и документация в формате XML. В конце главы дается краткое введение в основные области профессионального применения С#: ASP.NET (веб-формы и веб-службы) и ADO.NET (базы данных).
Указатели, без которых не мыслят свою жизнь программисты, использующие С и C++, в языке С# рекомендуется применять только в случае необходимости, поскольку они сводят на нет многие преимущества этого языка. Документирование кода в формате XML и регулярные выражения применяются шире, но относятся скорее к дополнительным возможностям языка, поэтому не были рассмотрены ранее.
Напротив, веб-формы, веб-службы и работа с базами данных являются одними из основных областей применения С#, но не рассматриваются в этой книге из-за того, что подобные темы обычно не входят в базовый курс программирования, поскольку для их полноценного освоения требуется иметь базовые знания в области сетей, баз данных, протоколов передачи данных и т. п.
Одним из основных достоинств языка С# является его схема работы с памятью: автоматическое выделение памяти под объекты и автоматическая уборка мусора. При этом невозможно обратиться по несуществующему адресу памяти или выйти за границы массива, что делает программы более надежными и безопасными и исключает возможность появления целого класса ошибок, доставляющих массу неудобств при написании программ на других языках.
Однако в некоторых случаях возникает необходимость работать с адресами памяти непосредственно, например, при взаимодействии с операционной системой, написании драйверов или программ, время выполнения которых критично. Такую возможность предоставляет так называемый небезопасный (unsafe) код.
Небезопасным называется код, выполнение которого среда CLR не контролирует. Он работает напрямую с адресами областей памяти посредством указателей и должен быть явным образом помечен с помощью ключевого слова unsafe, которое определяет так называемый небезопасный контекст выполнения.
Ключевое слово unsafe может использоваться либо как спецификатор, либо как оператор. В первом случае его указывают наряду с другими спецификаторами при описании класса, делегата, структуры, метода, поля и т. д. — везде, где допустимы другие спецификаторы. Это определяет небезопасный контекст для описываемого элемента, например:
public unsafe struct Node
{
public int Value;
public Node* Left;
Node* Right;
Вся структура Node помечается как небезопасная, что делает возможным использование в ней указателей Left и Right. Можно применить и другой вариант описания, в котором небезопасными объявляются только соответствующие поля структуры:
public struct Node
{
public int Value;
public unsafe Node* Left;
public unsafe Node* Right; }
Оператор unsafe имеет следующий синтаксис:
unsafe блок
Все операторы, входящие в блок, выполняются в небезопасном контексте.
ПРИМЕЧАНИЕ_______________________________________________________________
Компиляция кода, содержащего небезопасные фрагменты, должна производиться с ключом /unsafe. Этот режим можно установить путем настройки среды Visual Studio (Project ► Properties ► Configuration Properties ► Build ► Allow Unsafe Code).
_____________________________________________________________________________
Синтаксис указателей
Указатели предназначены для хранения адресов областей памяти. Синтаксис объявления указателя:
тип* переменная;
Здесь тип — это тип величины, на которую указывает переменная, то есть величины, хранящейся по записанному в переменной адресу. Тип не может быть классом, но может быть структурой, перечислением, указателем, а также одним из стандартных типов: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool и void. Последнее означает, что указатель ссылается на переменную неизвестного типа.
Указатель на тип voi d применяется в тех случаях, когда конкретный тип объекта, адрес которого требуется хранить, не определен (например, если в одной и той же переменной в разные моменты времени требуется хранить адреса объектов различных типов).
Указателю на тип voi d можно присвоить значение указателя любого типа, а также сравнивать его с любыми указателями, но перед выполнением каких-либо действий с областью памяти, на которую он ссылается, требуется преобразовать его к конкретному типу явным образом.
Примеры объявления указателей:
int* a; // указатель на int
Node* pNode; // указатель на описанную ранее структуру Node
void* p: // указатель на неопределенный тип
int*[] m: // одномерный массив указателей на int
int** d; // указатель на указатель на int
В одном операторе можно описать несколько указателей одного и того же типа, например:
int* a, b, с; //три указателя на int
Указатели являются отдельной категорией типов данных. Они не наследуются от типа object, и преобразование между типом object и типами указателей невозможно. В частности, для них не выполняется упаковка и распаковка. Однако допускаются преобразования между разными типами указателей, а также указателями и целыми.
ПРИМЕЧАНИЕ_______________________________________________________________
Именно потому что указатели могут содержать адрес любой области памяти и, следовательно, изменить ее, операции с ними и называются небезопасными.
Величины типа указателя могут являться локальными переменными, полями, параметрами и возвращаемым значением функции. Эти величины подчиняются общим правилам определения области действия и времени жизни.
Преобразования указателей
Для указателей поддерживаются неявные преобразования из любого типа указателя к типу void*. Любому указателю можно присвоить константу null. Кроме того, допускаются явные преобразования:
¨ между указателями любого типа;
¨ между указателями любого типа и целыми типами (со знаком и без знака).
Корректность преобразований лежит на совести программиста. Преобразования никак не влияют на величины, на которые ссылаются указатели, но при попытке получения значения по указателю несоответствующего типа поведение программы не определено.
Инициализация указателей
Ниже перечислены способы присваивания значений указателям:
1. Присваивание указателю адреса существующего объекта:
О с помощью операции получения адреса:
int a = 5; // целая переменная
int* р = &а; //в указатель записывается адрес а
ПРИМЕЧАНИЕ_______________________________________________________________
Программист должен сам следить за тем, чтобы переменные, на которые ссылается указатель, были правильно инициализированы.
_____________________________________________________________________________
О с помощью значения другого инициализированного указателя:
int* Г = р;
О с помощью имени массива, которое трактуется как адрес:
int[] b = new int[] {10, 20, 30, 50}: // массив
fixed ( int* t = b ){...}; // присваивание адреса начала массива
fixed ( int* t = &b[0] ){...}; // то же самое
ПРИМЕЧАНИЕ_______________________________________________________________
Оператор f i xed рассматривается позже.
_____________________________________________________________________________
2. Присваивание указателю адреса области памяти в явном виде:
char* v = (char *)0xl2F69E;
Здесь 0xl2F69E — шестнадцатеричная константа, (char *) — операция приведения типа: константа преобразуется к типу указателя на char.
ПРИМЕЧАНИЕ_______________________________________________________________Использовать этот способ можно только в том случае, если адрес вам точно известен, в противном случае может возникнуть исключение.
_____________________________________________________________________________
3. Присваивание нулевого значения:
int* хх = null;
4. Выделение области памяти в стеке и присваивание ее адреса указателю:
int* s = stackailoc int [10];
Здесь операция stackal loc выполняет выделение памяти под 10 величин типа int (массив из 10 элементов) и записывает адрес начала этой области памяти в переменную s, которая может трактоваться как имя массива.
ПРИМЕЧАНИЕ_______________________________________________________________
Специальцрх операций для выделения области динамической памяти (хипа) в С# не предусмотрено. В случае необходимости можно использовать, например, системную функцию HeapAl loc (пример см. в спецификации языка).
Операции с указателями
Все операции с указателями выполняются в небезопасном контексте. Они перечислены в табл. 15.1.
Рассмотрим примеры применения операций. Если в указатель занесен адрес объекта, получить доступ к этому объекту можно с помощью операций разадреса-ции и доступа к элементу.
Операция разадресации, или разыменования, предназначена для доступа к величине, адрес которой хранится в указателе. Эту операцию можно использовать как для получения, так и для изменения значения величины, например:
fixed ( int* t = b ) // инициализация указателя адресом начала массива
{
int* z = t; // инициализация указателя значением другого указателя
for (int i = 0; i < b.Length; ++i )
{
t[i] += 5; // доступ к элементу массива (увеличение на 5)
*z += 5; // доступ с помощью разадресации (увеличение еще на 5)
++z; // инкремент указателя
}
Console.WriteLine( &t[5] - t ); // операция вычитания указателей
}
Оператор fixed фиксирует объект, адрес которого заносится в указатель, для того чтобы его не перемещал сборщик мусора и, таким образом, указатель остался корректным. Фиксация происходит на время выполнения блока, который записан после круглых скобок.
В приведенном примере доступ к элементам массива выполняется двумя способами: путем индексации указателя t и путем разадресации указателя z, значение которого инкрементируется при каждом проходе цикла для перехода к следующему элементу массива.
Конструкцию *переменная можно использовать в левой части оператора присваивания, так как она определяет адрес области памяти. Для простоты эту конструкцию можно считать именем переменной, на которую ссылается указатель. С ней допустимы все действия, определенные для величин соответствующего типа.
Арифметические операции с указателями (сложение с целым, вычитание, инкремент и декремент) автоматически учитывают размер типа величин, адресуемых указателями. Эти операции применимы только к указателям одного типа и имеют смысл в основном при работе со структурами данных, элементы которых размещены в памяти последовательно, например, с массивами.
Инкремент перемещает указатель к следующему элементу массива, декремент — к предыдущему. Фактически значение указателя изменяется на величину sizeof (тип), где sizeof — операция получения размера величины указанного типа (в байтах). Эта операция применяется только в небезопасном контексте, с ее помощью можно получать размеры не только стандартных, но и пользовательских типов данных. Для структуры результат может быть больше суммы длин составляющих ее полей из-за выравнивания элементов.
Если указатель на определенный тип увеличивается или уменьшается на константу, его значение изменяется на величину этой константы, умноженную на размер объекта данного типа, например:
short* р; ...
р++; // значение р увеличивается на 2
long* q; ...
q++: // значение q увеличивается на 4
Разность двух указателей — это разность их значений, деленная на размер типа в байтах. Так, результат выполнения последней операции вывода в приведенном примере равен 5. Суммирование двух указателей не допускается.
При записи выражений с указателями следует обращать внимание на приоритеты операций. В качестве примера рассмотрим последовательность действий, заданную в операторе
*р++ = 10;
Поскольку инкремент постфиксный, он выполняется после выполнения операции присваивания. Таким образом, сначала по адресу, записанному в указателе р, будет записано значение 10, а затем указатель увеличится на количество байтов, соответствующее его типу. То же самое можно записать подробнее:
*р = 10; р++;
Выражение (*р)++, напротив, инкрементирует значение, на которое ссылается указатель.
В следующем примере каждый байт беззнакового целого числа х выводится на консоль с помощью указателя t:
uint x = 0xAB10234F;
byte* t = (byte*)&x;
for ( int i = 0; i < 4; ++i )
Console.Write("{0;X} ". *t++ ); // результат: 4F 23 10 AB
Как видите, первоначально указатель t был установлен на младший байт переменной х. Листинг 15.1 иллюстрирует доступ к полю класса и элементу структуры:
Листинг 15.1. Доступ к полю класса и элементу структуры с помощью указателей
using System;
namespace ConsoleAppli cati onl
{
class A
{ public int value = 20; }
struct В ;
{ public int a; }
class Program
{ unsafe static void Main( )
{
A n = new A( ); .
fixed ( int* pn = &n.value ) ++(*pn);
Console.WriteLineC "n - " + n.value ); // результат; 21
В b;
В* pb = &b;
pb->a =100;
Console.WriteLineC b.a ); // результат: 100
Операция stackalloc позволяет выделить память в стеке под заданное количество величин заданного типа:
stackalloc тип [ количество ]
Количество задается целочисленным выражением. Если памяти недостаточно, генерируется исключение System.StackOverflowException. Выделенная память ничем не инициализируется и автоматически освобождается при завершении блока, содержащего эту операцию. Пример выделения памяти под пять элементов типа int и их заполнения числами от 0 до 4:
int* p = stackalioc.int [5];
for ( int i = 0; i < 5; ++i )
p[i]=i;
Console.Write( p[i] + " " ); // результат: 0 12 3 4
}
В листинге 15.2 приведен пример работы с указателями, взятый из спецификации С#. Метод IntToString преобразует переданное ему целое значение в строку символов, заполняя ее посимвольно путем доступа через указатель.
Листинг 15.2. Пример работы с указателями: перевод числа в строку
using System;
class Test
{
static string IntToString ( int value )
{
int n = value >= 0 ? value : -value;
unsafe {
char* buffer = stackalloc char[16];
char* p = buffer +16;
do {
*--p = (char)( n % 10 + '0' );
n /= 10;
} while ( n != 0 );
if ( value < 0 ) *--p = '-';
return new stringC p, 0, (int)( buffer + 16 - p ) ); |
}
static void Main( ) {
Console.WriteLineC IntToStringC 12345 ) ):
Console.WriteLineC IntToStringC -999 ) );
Регулярные выражения предназначены для обработки текстовой информации и обеспечивают:
¨ эффективный поиск в тексте по заданному шаблону;
¨ редактирование, замену и удаление подстрок;
¨ формирование итоговых отчетов по результатам работы с текстом.
С помощью регулярных выражений удобно обрабатывать, например, файлы в формате HTML, файлы журналов или длинные текстовые файлы. Для поддержки регулярных выражений в библиотеку .NET включены классы, объединенные в пространство имен System.Text.RegularExpressions.
Метасимволы
Регулярное выражение — это шаблон (образец), по которому выполняется поиск соответствующего ему фрагмента текста. Язык описания регулярных выражений состоит из символов двух видов: обычных и метасимволов. Обычный символ представляет в выражении сам себя, а метасимвол — некоторый класс символов, например любую цифру или букву.
Например, регулярное выражение для поиска в тексте фрагмента «Вася» записывается с помощью четырех обычных символов Вася, а выражение для поиска двух цифр, идущих подряд, состоит из двух метасимволов \d\d.
С помощью комбинаций метасимволов можно описывать сложные шаблоны для поиска. Например, можно описать шаблон для IP-адреса, адреса электронной почты, различных форматов даты, заголовков определенного вида и т. д.
ПРИМЕЧАНИЕ_______________________________________________________________
Синтаксис регулярных выражений .NET в основном позаимствован из языка Perl 5. Неподготовленного человека вид сложного регулярного выражения может привести в замешательство, но при вдумчивом изучении он обязательно почувствует его красоту и очарование. Пожалуй, регулярные выражения более всего напоминают заклинания, по которым волшебным образом преобразуется текст. Ошибка всего в одном символе делает заклинание бессильным, зато, верно составленное, оно творит чудеса!
В табл. 15.2 описаны наиболее употребительные метасимволы, представляющие собой классы символов.
Метасимволы, перечисленные в табл. 15.3, уточняют позицию в строке, в которой следует искать совпадение с регулярным выражением, например, только в начале или в конце строки. Эти метасимволы являются мнимыми, то есть в тексте им не соответствует никакой реальный символ.
Например, выражение ˆcat соответствует символам cat, встречающимся в начале строки, выражение cat$ — символам cat, встречающимся в конце строки (то есть за ними идет символ перевода строки), а выражение ˆ$ представляет пустую строку, то есть начало строки, за которым сразу же следует ее конец.
В регулярных выражениях часто используют повторители. Повторители — это метасимволы, которые располагаются непосредственно после обычного символа или класса символов и задают количество его повторений в выражении. Например, если требуется записать выражение для поиска в тексте пяти идущих подряд цифр, вместо метасимволов \d\d\d\d\d можно записать \d{5}. Такому выражению будут соответствовать фрагменты 11111, 12345, 53332 и т. д.
Наиболее употребительные повторители перечислены в табл. 15.4.
Помимо рассмотренных элементов регулярных выражений можно использовать конструкцию выбора из нескольких элементов. Варианты выбора перечисляются
через вертикальную черту. Например, если требуется определить, присутствует ли в тексте хотя бы один элемент из списка «cat», «dog» и «horse», можно использовать выражение
cat|dog|horse
При поиске используется так называемый «ленивый» алгоритм, по которому поиск прекращается при нахождении самого короткого из возможных фрагментов, совпадающих с регулярным выражением.
Примеры простых регулярных выражений:
¨ целое число (возможно, со знаком):
[-+]?\d+
¨ вещественное число (может иметь знак и дробную часть, отделенную точкой):
[-+]?\d+\.?\d *
¨ российский номер автомобиля (упрощенно):
[A-Z]\d{3}[A-Z]{2}\d\dRUS
ВНИМАНИЕ _____________________________________________________________
Если требуется описать в выражении обычный символ, совпадающий с каким-либо метасимволом, его предваряют обратной косой чертой. Так, для поиска в тексте символа точки следует записать \., а для поиска косой черты — \\.
_____________________________________________________________________________
Например, для поиска в тексте имени файла cat.doc следует использовать регулярное выражение cat\.doc. Символ «точка» экранируется обратной косой чертой для того, чтобы он воспринимался не как метасимвол «любой символ» (в том числе и точка!), а непосредственно.
Для группирования элементов выражения используются круглые скобки. Группирование применяется во многих случаях, например, если требуется задать повторитель не для отдельного символа, а для последовательности (это использовано в предыдущей таблице). Кроме того, группирование служит для запоминания в некоторой переменной фрагмента текста, совпавшего с выражением, заключенным в скобки. Имя переменной задается в угловых скобках или апострофах:
(?<имя_переменной>фрагмент_выражения)
Фрагмент текста, совпавший при поиске с фрагментом регулярного выражения, заносится в переменную с заданным именем. Пусть, например, требуется выделить из текста номера телефонов, записанных в виде nnn-nn-nn. Регулярное выражение для поиска номера можно записать так:
(?<num>\d\d\d-\d\d-\d\d)
При анализе текста в переменную с именем num будут последовательно записываться найденные номера телефонов.
Рассмотрим еще один вариант применения группирования — для формирования обратных ссылок. Все конструкции, заключенные в круглые скобки, автоматически нумеруются, начиная с 1. Эти номера, предваренные обратной косой чертой, можно использовать для ссылок на соответствующую конструкцию. Например, выражение (\w)\l используется для поиска сдвоенных символов в словах (wall, mass, cooperates).
Круглые скобки могут быть вложенными, при этом номер конструкции определяется порядком открывающей скобки в выражении. Примеры:
В этом выражении три подвыражения, заключенных в скобки. Ссылка на первое из них выполняется в конце выражения. С этим выражением совпадут, например, фрагменты
Вася должен 5 руб. Ну что же ты, Вася
Вася должен 53459 руб. Ну что же ты. Вася
Выражение, задающее IP-адрес:
((\d{l,3}\.){3}\d{1.3})
Адрес состоит из четырех групп цифр, разделенных точками. Каждая группа может включать от одной до трех цифр. Примеры IP-адресов: 212.46.197.69, 212.194.5.106, 209.122.173.160. Первая группа, заключенная в скобки, задает весь адрес. Ей присваивается номер 1. В нее вложены вторые скобки, определяющие границы для повторителя {3}.
Переменную, имя которой задается внутри выражения в угловых скобках, также можно использовать для обратных ссылок в последующей части выражения. Например, поиск двойных символов в словах можно выполнить с помощью выражения (?<s>\w)\k<s>, где s — имя переменной, в которой запоминается символ, \k — элемент синтаксиса.
В регулярное выражение можно помещать комментарии. Поскольку выражения обычно проще писать, чем читать, это — очень полезная возможность. Комментарий либо помещается внутрь конструкции (?# ), либо располагается, начиная от символа # до конца строки.
Классы библиотеки .NET для работы
с регулярными выражениями
Классы библиотеки .NET для работы с регулярными выражениями объединены в пространство имен System.Text.RegularExpressions.
Начнем с класса Regex, представляющего собственно регулярное выражение. Класс является неизменяемым, то есть после создания экземпляра его корректировка не допускается. Для описания регулярного выражения в классе определено несколько перегруженных конструкторов:
¨ Regex () — создает пустое выражение;
¨ Regex(String) — создает заданное выражение;
¨ RegexCString, RegexOptions) — создает заданное выражение и задает параметры для его обработки с помощью элементов перечисления RegexOptions (например, различать или не различать прописные и строчные буквы).
Пример конструктора, задающего выражение для поиска в тексте повторяющихся слов, расположенных подряд и разделенных произвольным количеством пробелов, независимо от регистра:
Regex гх = new Regex( @"\b(?<word>\w+)\s+(\k<word>)\b",
RegexOptions.IgnoreCase );
Поиск фрагментов строки, соответствующих заданному выражению, выполняется с помощью методов IsMatch, Match и Matches.
Метод IsMatch возвращает true, если фрагмент, соответствующий выражению, в заданной строке найден, и false в противном случае. В листинге 15.3 приведен пример поиска повторяющихся слов в двух тестовых строках. В регулярное выражение, приведенное ранее, добавлен фрагмент, позволяющий распознавать знаки препинания.
Листинг 15.3. Поиск в строке дублированных слов (методом IsMatch)
using System; using System.Text.RegularExpressions;
public class Test
{ public static void Main( )
{
Regex г = new Regex( @"\b(?<word>\w+)[.,:;!? ]\s*(\k<word>)\b",
RegexOptions.IgnoreCase );
string tstl = "Oh, oh! Give me more!";
if ( r.IsMatch( tstl ) ) Console.WriteLineC " tstl yes" );
else Console.WriteLine( " tstl no" );
string tst2 = "Oh give me, give me more!";
if ( r.IsMatch( tst2 ) ) Console.WriteLine( " tst2 yes" );
else Console.WriteLineC " tst2 no" );
}
Результат работы программы:
tstl yes
tst2 no
Повторяющиеся слова в строке tst2 располагаются не подряд, поэтому она не соответствует регулярному выражению. Для поиска повторяющихся слов, расположенных в произвольных позициях строки, в регулярном выражении нужно всего-навсего заменить пробел (\s) «любым символом» (.):
Regex г = new Regex( @"\b(?<word>\w+)[.,:;!? ].*(\k<word>)\b".
RegexOptions.IgnoreCase );
Метод Match класса Regex, в отличие от метода IsMatch, не просто определяет, произошло ли совпадение, а возвращает объект класса Match — очередной фрагмент, совпавший с образцом. Рассмотрим листинг 15.4, в котором используется этот метод.
Листинг 15.4. Выделение из строки слов и чисел (методом Match)
using System;
using System.Text.RegularExpressions;
public class Test
{
public static void Main( )
{
string text = "Салат - $4, борщ - $3, одеколон - $10.";
string pattern = @"(\w+) - \$(\d+)[.,]";
Regex г = new Regex( pattern );
Match m = r.Match( text );
int total = 0;
while ( m.Success )
{ Console.WriteLineC m );
total += int.ParseC m.Groups[2].ToString() );
m = m.NextMatch( );
}
Console.Writel_ine( "Итого: $" + total );
Результат работы программы:
Салат - $4,
борщ - $3.
одеколон - $10.
Итого: $17
При первом обращении к методу Match возвращается первый фрагмент строки, совпавший с регулярным выражением pattern. В классе Match определено свойство Groups, возвращающее коллекцию фрагментов, совпавших с подвыражениями в круглых скобках. Нулевой элемент коллекции содержит весь фрагмент, первый элемент — фрагмент, совпавший с подвыражением в первых скобках, второй элемент — фрагмент, совпавший с подвыражением во вторых скобках, и т. д. Если при определении выражения задать фрагментам имена, как это было сделано в предыдущем листинге, можно будет обратиться к ним по этим именам, например:
string pattern = @"(?'name'\w+) - \$(?'price'\d+)[.,]";
total += int.Parse( m.Groups["price"].ToString( ) );
ПРИМЕЧАНИЕ ______________________________________________________________
Метод NextMatch класса Match продолжает поиск в строке с того места, на котором закончился предыдущий поиск.
_____________________________________________________________________________
Метод Matches класса Regex возвращает объект класса MatchCollection — коллекцию всех фрагментов заданной строки, совпавших с образцом.
Рассмотрим теперь пример применения метода Split класса Regex. Этот метод разбивает заданную строку на фрагменты в соответствии с разделителями, заданными с помощью регулярного выражения, и возвращает эти фрагменты в массиве строк. В листинге 15.5 строка из листинга 15.4 разбивается на отдельные слова.
Листинг 15.5. Разбиение строки на слова (методом Split)
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
public class Test
{ public static void MainO
{
string text = "Салат - $4. борщ -$3, одеколон - $10.";
string pattern = "[- ,.]+";
Regex r = new Regex( pattern );
List<string> words = new List<string>( r.Split( text ) );
foreach ( string word in words ) Console.WriteLineC word );
Результат работы программы:
Салат
$4
борщ
$3
одеколон
$10
Метод Repl асе класса Regex позволяет выполнять замену фрагментов текста. Определено несколько перегруженных версий этого метода. Вот как выглядит пример простейшего применения метода в его статическом варианте, заменяющего все вхождения символа $ символами у. е.:
string text = "Салат - $4, борщ -$3, одеколон - $10.";
string textl = Regex.Replace( text, @"\$", "y.e." );
Другие версии метода позволяют задавать любые действия по замене с помощью делегата MatchEvaluator, который вызывается для каждого вхождения фрагмента, совпавшего с заданным регулярным выражением.
ПРИМЕЧАНИЕ_______________________________________________________________
Помимо классов Regex и Match в пространстве имен System.Text.RegularExpressions определены вспомогательные классы, например, класс Capture — фрагмент, совпавший с подвыражением в круглых скобках; класс CaptureCol lection — коллекция фрагментов, совпавших со всеми подвыражениями в текущей группе; класс Group содержит коллекцию Capture для текущего совпадения с регулярным выражением и т. д.
_____________________________________________________________________________
В качестве более реального примера применения регулярных выражений рассмотрим программу анализа файла журнала веб-сервера. Это текстовый файл, каждая строка которого содержит информацию об одном соединении с сервером. Четыре строки файла приведены ниже:
ppp-48.poo1-113.spbnit.ru - - [31/Мау/2002:02:08:32 +0400] "GET / HTTP/1.1" 200
2434 "http://www.price.ru/bin/price/firminfo_f?fid=10922&where=01&base=2"
"Mozilia/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
81.24.130.7 - - [31/May/2002:08:13:17 +0400] "GET /swf/menu.swf HTTP/1.1" 200
4682 "-" "Mozilla/4.0 (compatible; MSIE 5.01; Windows 98)"
81.24.130.7 - r [31/May/2002:08:13:17 +0400] "GET /swf/header.swf HTTP/1.1" 200
21244 "-" "Mozilla/4.0 (compatible; MSIE 5.01; Windows 98)"
gate.solvo.ru - - [31/May/2002:10:43:03 +0400] "GET / HTTP/1.0" 200 2422
"http://www.price.ru/bin/price/firminfo_f?fid=10922&where=01&base=l"
"Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)"
Подобные файлы могут иметь весьма значительный объем, поэтому составление итогового отчета в удобном для восприятия формате имеет важное значение. Если рассматривать каждую строку файла как совокупность полей, разделенных пробелами, то поле номер 0 содержит адрес, с которого выполнялось соединение с сервером, поле номер 5 — операцию (GET при загрузке информации), поле 8 — признак успешности выполнения операции (200 — успешно) и, наконец, поле
9 — количество переданных байтов.
Приведенная в листинге 15.6 программа формирует в формате HTML итоговый отчет, содержащий таблицу адресов, с которых выполнялось обращение к серверу, и суммарное количество переданных байтов для каждого адреса.
Листинг 15.6. Анализ журнала веб-сервера
using System;
using System.10;
using System.Col lections.Generic;
using System.Text.RegularExpressions;
public class Test
{ public static void MainO
StreamReader f = new StreamReaderC "accessjog" );
StreamWriter w = new StreamWriterC "report.htrn" );
Regex get = new Regex( "GET" );
Regex г = new Regex( " " );
string s. entry;
int value:
string[] items = new ftring[40]:
Dictionary<string, int> table = new Dictionary<string, int>( ):
while ( (s = f.ReadLine( ) ) != null )
{
items = r.SplitC s );
if ( get.IsMatchC items[5] ) && items[8] == "200" )
{
entry = iterns[0];
value = int.Parse( items[9] );
if ( table.ContainsKey( entry ) ) table[entry] += value;
else table[entry] = value;
f.Close( );
w.Write( "<html><head><title> Report </title></head><body>" +
"<table border =1 <tr><td> Computer <td> Bytes </tr>"
foreach ( string item in table.Keys )
w.Write( "<tr><td>{0}<td>{l}</tr>", item, table[item] );
w.Write( "</table></body>" );
w.Close();
Фрагмент результата работы программы показан на рис. 15.1.
Рис. 15.1. Фрагмент журнала веб-сервера
Файл access_log считывается построчно, каждая строка разбивается на поля, которые заносятся в массив items. Затем, если загрузка прошла успешно, о чем свидетельствуют значения GET и 200 полей 5 и 8, количество переданных байтов(поле 9) преобразуется в целое и заносится в хеш-таблицу по ключу, которым служит адрес, хранящийся в поле 0.
Для формирования HTML-файла report.htm используются соответствующие теги. Файл можно просмотреть, например, с помощью Internet Explorer. Как видите, программа получилась весьма компактной за счет использования стандартных классов библиотеки .NET.
Документирование в формате XML
XML (extensible Markup Language) — это язык разметки текста. Разметкой является все, что не относится к содержанию, или, как модно говорить, контенту: структура документа, формат, вид и т. д. Разметка осуществляется с помощью тегов — управляющих элементов, заключенных в угловые скобки. Теги в XML всегда парные: открывающий тег записывается перед размечаемым фрагментом, а закрывающий — после него. Закрывающий тег выглядит так же, как открывающий, но предваряется косой чертой, например:
<summary> Класс для работы с регулярными выражениями </summary>
В тегах могут присутствовать атрибуты — элементы вида имя - значение, уточняющие и дополняющие описание элемента.
Язык XML широко распространен в Интернете благодаря его универсальности и переносимости. Корпоративные приложения используют XML как основной формат для обмена данными. Строго говоря, XML является не языком, а системой правил для описания языков.
ПРИМЕЧАНИЕ_______________________________________________________________
Многие составляющие технологии .NET неразрывно связаны с XML, поэтому в пространстве имен System. Xml библиотеки .NET описано множество классов, поддерживающих XML. Объем и задача учебника не позволяют описать эти классы и технологии.
_____________________________________________________________________________
Любой программный продукт требуется документировать. Соответствие версий документации и программы — серьезная проблема, которая решается в .NET встраиванием документации прямо в код программы с помощью комментариев и XML-тегов.
Комментарии, используемые для построения файлов документации, начинаются с символов ///и размещаются перед соответствующим элементом программы. Внутри комментариев записываются теги, относящиеся к комментируемому элементу — классу, методу, параметру метода и т. п. Теги перечислены в табл. 15.5.
Для построения файлов документации в формате XML требуется использовать режим компиляции /doc: имя_ файла.xml. Этот режим можно установить и в среде Visual Studio в окне свойств проекта (Project ► Properties) на вкладке Build (задать имя файла XML documentation file). Компилятор проверяет правильность записи тегов и их соответствие элементам программы.
ПРИМЕЧАНИЕ_______________________________________________________________
В Visual Studio 2003 для построения файлов документации в формате HTML используется команда меню Tools > Build Comment Web Pages.
Темы, не рассмотренные в книге
Эта книга заканчивается там, где начинается самое интересное — профессиональное применение С#: создание веб-форм и веб-служб, распределенных приложений, работа с базами данных с помощью ADO.NET и т. д. Этим вопросам посвящены тысячи страниц книг и документации, а здесь приводится краткое введение для того, чтобы облегчить вам выбор материала для дальнейшего изучения.
ADO.NET
Работа с данными является одной из главных задач при создании как сетевых, так и автономных приложений. Библиотека .NET содержит богатый набор средств под общим названием ADO.NET (ActiveX Data Objects), поддерживающих взаимодействие с локальными и удаленными хранилищами данных.
Объектная модель ADO.NET состоит из классов двух видов: компоненты сущностей (content components) и компоненты управляемых поставщиков (managed-provider components). Основным классом первого вида является класс DataSet, представляющий собой набор связанных таблиц — локальную копию базы данных или ее части. Кроме того, определены вспомогательные классы DataTable, DataRow, DataColumn и DataRelation. В классах этого вида располагаются пересылаемые данные. Класс DataSet может содержать несколько объектов DataTable и DataRelation. В классе DataSet описан набор методов, интегрирующих его с XML, что делает возможным межплатформенное взаимодействие.
Компоненты управляемых поставщиков обеспечивают интерфейс для доступа к данным (извлечения и обновления). Для непосредственной работы с данными используются объекты Connection, Command и DataReader. Класс DataAdapter играет роль канала передачи данных между хранилищем и компонентами сущностей.
Данные могут представлять собой выборку из базы данных, XML-файл или, например, таблицу Excel.
Классы ADO.NET предназначены для решения следующих задач:
□ установления соединения с хранилищем данных;
□ создания и заполнения данными объекта DataSet;
□ отключения от хранилища данных;
□ возврата изменений, внесенных в DataSet, обратно в хранилище данных.
Классы ADO.NET определены в пространствах имен System.Data, System.Data.Common, System.Data.01 eDb, System.Data.SqlClient и System.Data.SqlTypes.
Среда Visual Studio .NET располагает средствами, упрощающими программирование баз данных. В среду включена копия ядра MSDE. С помощью окна Server Explorer (View ► Server Explorer) можно подключиться к SQL Server в локальной или удаленной системе. После подключения можно выполнять различные операции с базами данных, таблицами и хранимыми процедурами.
ASP.NET
Под термином ASP.NET (Active Server Pages for .NET) объединяются все средства поддержки веб-серверов в .NET, включая веб-страницы и веб-службы.
Сервер — это аппаратный или программный компонент вычислительной системы, выполняющий специализированные функции по запросу клиента, предоставляя
ему доступ к определенным ресурсам. Сервер, реализованный в виде программы или программного модуля, обычно решает строго определенную задачу и обменивается информацией с клиентом по определенному протоколу. Примеры программных серверов: FTP-сервер, веб-сервер (Apache, IIS), сервер баз данных, почтовый сервер.
Веб-сервер — это сервер, предоставляющий доступ к сайтам World Wide Web. Когда пользователь дает браузеру команду открыть документ на некотором сайте, браузер подключается к соответствующему серверу и запрашивает у него содержимое документа. Обычно веб-сервер работает по протоколам HTTP и/или HTTPS. На сегодня наиболее распространенными веб-серверами являются:
□ Apache (свободно распространяемый веб-сервер с открытым исходным кодом; наиболее часто используется в Unix-подобных операционных системах);
□ IIS (Internet Information Services) от компании Microsoft.
ПРИМЕЧАНИЕ_______________________________________________________________
IIS поставляется Microsoft как часть операционной системы, но по умолчанию в Windows 2000 Professional не устанавливается. Для установки IIS воспользуйтесь командой меню Пуск ► Настройка ► Панель управления ► Установка и удаление программ ► Установка компонентов Windows. После этого потребуется зарегистрировать его с помощью утилиты aspnetjregiis, следуя инструкциям справочной службы.
Веб-приложение — это набор взаимосвязанных файлов, расположенных на IIS-сервере в своем виртуальном каталоге, которому соответствует физический каталог на диске. Файлы веб-страниц имеют расширение aspx. Для создания веб-приложения следует выбрать шаблон ASP.NET Web Application. Обратите внимание на то, что в поле Location записан URL-адрес компьютера, а не путь к каталогу на диске. Вид среды после создания проекта практически такой же, как и при создании Windows-приложения, однако для разработки интерфейса веб-страницы используются элементы категории Web Form Controls, основанные на HTML-коде, а не категории Windows Forms.
Интерактивная веб-страница создается так же, как обычное Windows-приложение: перетаскиванием элементов управления с панели инструментов на форму, настройкой их характеристик в окне свойств и заданием реакции на события. Среда автоматически создает файл для генерации HTML-кода с расширением aspx (его можно просмотреть на вкладке HTML окна редактора кода) и связанный с ним файл на языке С# с расширением aspx.cs. В этом файле расположено описание класса, являющегося потомком System.Web.UI.Раде. Страница (aspx-файл) содержит ссылку на этот класс. Когда клиент запрашивает страницу, среда выполнения ASP.NET создает экземпляр класса.
Возможность применения стандартных элементов управления из категории Web Form Controls является одним из важнейших достоинств ASP.NET, поскольку при этом значительно упрощается создание пользовательского интерфейса на веб-страницах. С элементами управления можно работать и как с обычными классами С#, и через aspx-файл. В каждом элементе определены набор событий, которые будут обрабатываться на сервере, и средства проверки ввода данных пользователем.
ПРИМЕЧАНИЕ_______________________________________________________________
В ASP.NET есть два элемента, DataGrid и DataLiSt, которые предназначены для отображения данных, полученных из источника (обычно это объект ADO DataSet). С помощью этих объектов можно создать приложение для решения одной из часто встречающихся задач — найти в каком-либо источнике данные по запросу пользователя и вернуть их в виде таблицы.
_____________________________________________________________________________
С помощью XML можно создавать программные компоненты, которые взаимодействуют друг с другом независимо от языка и платформы. Веб-службы обеспечивают доступ к программным компонентам через стандартные протоколы Интернета, такие как HTTP и SMTP.
Веб-служба .NET — это модуль кода .NET, который обычно устанавливается на IIS-сервере. Веб-служба строится из тех же типов, что и любая сборка .NET: классов, интерфейсов, перечислений и структур, которые для клиента представляют собой «черный ящик», отвечающий на запросы. Службы предназначены для обработки удаленных вызовов, поэтому у них обычно отсутствует графический интерфейс пользователя.
Одно из значений термина «служба» в обычной жизни — это, например, справочная служба или служба быта, когда мы по запросу получаем какую-либо услугу от поставщика услуг. В программном обеспечении службой называется блок кода, способный выполнить какие-либо действия по запросу пользователя (считать данные из источника, выполнить вычисления) и ждать следующего запроса. Веб-службы могут использоваться любым приложением, умеющим разбирать XML-поток, переданный по протоколу HTTP.
Веб-служба, как и обычное приложение ASP.NET, традиционно располагается в виртуальном каталоге на IIS-сервере. Файл веб-службы имеет расширение asmx. В нем так же, как и в aspx-файле, содержится ссылка на кодовый файл на языке С# с расширением asm.cs, в котором и находится собственно код веб-службы. Класс, обеспечивающий работу веб-службы, является потомком класса System. Web. Services.WebService. Для создания веб-службы используется шаблон проекта ASP. NET Web Service.
Чтобы использовать язык С# на профессиональном уровне, необходимо не только хорошо представлять себе его конструкции, но и изучить бесчисленные классы библиотеки .NET. В этом вам поможет электронная документация и книги, например, [13], [20], [21], [23], [27], [31]-[33]. Для понимания возможностей классов необходимо представлять себе основы построения сетей и баз данных, протоколы Интернета, HTML, XML и многое другое.
Желаю вам получить удовольствие от погружения в это бескрайнее море информации и от программирования на прекрасном языке! Ведь, говоря высоким слогом, познание и созидание — одни из самых сильных и глубоких радостей жизни.
Лабораторная работа 1. Линейные программы
Теоретический материал: главы 1-3.
Напишите программу для расчета по двум формулам. Предварительно подготовьте тестовые примеры с помощью калькулятора (результаты вычисления по обеим формулам должны совпадать). Класс Math, содержащий математические функции С#, описан на с. 64. Кроме того, для поиска нужной функции можно воспользоваться алфавитным указателем. Методы, отсутствующие в классе, выразите через имеющиеся.
Разветвляющиеся вычислительные процессы
Теоретический материал: глава 4, раздел «Операторы ветвления».
Задание 1. Вычисление значения функции
Написать программу, которая по введенному значению аргумента вычисляет значение функции, заданной в виде графика. Параметр R вводится с клавиатуры.
Задание 2. Попадание точки в заштрихованную область
Написать программу, которая определяет, попадает ли точка с заданными координатами в область, закрашенную на рисунке серым цветом. Результат работы программы вывести в виде текстового сообщения.
Организация циклов
Теоретический материал: глава 4, разделы «Операторы цикла», «Базовые конструкции структурного программирования».
Задание 1. Таблица значений функции
Вычислить и вывести на экран в виде таблицы значения функции, заданной графически (см. задание 1 лабораторной работы 2), на интервале от хнач до хкон с шагом dх. Интервал и шаг задать таким образом, чтобы проверить все ветви программы. Таблицу снабдить заголовком и шапкой.
Задание 2. Серия выстрелов по мишени
Для десяти выстрелов, координаты которых задаются с клавиатуры, вывести текстовые сообщения о попадании в мишень из задания 2 лабораторной работы 2.
Задание 3. Ряды Тейлора
Вычислить и вывести на экран в виде таблицы значения функции, заданной с помощью ряда Тейлора, на интервале от хнач до хкон с шагом dх с точностью е. Таблицу снабдить заголовком и шапкой. Каждая строка таблицы должна содержать значение аргумента, значение функции и количество просуммированных членов ряда.
Лабораторная работа 4. Простейшие классы
Теоретический материал: глава 4, раздел «Обработка исключительных ситуаций», глава 5.
Каждый разрабатываемый класс должен, как правило, содержать следующие элементы: скрытые поля, конструкторы с параметрами и без параметров, методы, свойства. Методы и свойства должны обеспечивать непротиворечивый, полный, минимальный и удобный интерфейс класса. При возникновении ошибок должны выбрасываться исключения.
В программе должна выполняться проверка всех разработанных элементов класса.
Вариант 1
Описать класс, реализующий десятичный счетчик, который может увеличивать или уменьшать свое значение на единицу в заданном диапазоне. Предусмотреть инициализацию счетчика значениями по умолчанию и произвольными значениями. Счетчик имеет два метода: увеличения и уменьшения, — и свойство, позволяющее получить его текущее состояние. При выходе за границы диапазона выбрасываются исключения.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 2
Описать класс, реализующий шестнадцатеричный счетчик, который может увеличивать или уменьшать свое значение на единицу в заданном диапазоне. Предусмотреть инициализацию счетчика значениями по умолчанию и произвольными значениями. Счетчик имеет два метода: увеличения и уменьшения, — и свойство,
позволяющее получить его текущее состояние. При выходе за границы диапазона выбрасываются исключения.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 3
Описать класс, представляющий треугольник. Предусмотреть методы для создания объектов, перемещения на плоскости, изменения размеров и вращения на заданный угол. Описать свойства для получения состояния объекта. При невозможности построения треугольника выбрасывается исключение.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 4
Построить описание класса, содержащего информацию о почтовом адресе организации. Предусмотреть возможность раздельного изменения составных частей адреса и проверки допустимости вводимых значений. В случае недопустимых значений полей выбрасываются исключения.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 5
Составить описание класса для представления комплексных чисел. Обеспечить выполнение операций сложения, вычитания и умножения комплексных чисел.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 6
Составить описание класса для вектора, заданного координатами его концов в трехмерном пространстве. Обеспечить операции сложения и вычитания векторов с получением нового вектора (суммы или разности), вычисления скалярного произведения двух векторов, длины вектора, косинуса угла между векторами.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 7
Составить описание класса прямоугольников со сторонами, параллельными осям координат. Предусмотреть возможность перемещения прямоугольников на плоскости, изменение размеров, построение наименьшего прямоугольника, содержащего два заданных прямоугольника, и прямоугольника, являющегося общей частью (пересечением) двух прямоугольников.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 8
Составить описание класса для представления даты. Предусмотреть возможности установки даты и изменения ее отдельных полей (год, месяц, день) с проверкой допустимости вводимых значений. В случае недопустимых значений полей выбрасываются исключения. Создать методы изменения даты на заданное количество дней, месяцев и лет.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 9
Составить описание класса для представления времени. Предусмотреть возможности установки времени и изменения его отдельных полей (час, минута, секунда) с проверкой допустимости вводимых значений. В случае недопустимых значений полей выбрасываются исключения. Создать методы изменения времени на заданное количество часов, минут и секунд.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 10
Составить описание класса многочлена вида ах2 + bх + с. Предусмотреть методы, реализующие:
□ вычисление значения многочлена для заданного аргумента;
□ операцию сложения, вычитания и умножения многочленов с получением нового объекта-многочлена;
□ вывод на экран описания многочлена.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 11
Описать класс, представляющий треугольник. Предусмотреть методы для создания объектов, вычисления площади, периметра и точки пересечения медиан. Описать свойства для получения состояния объекта. При невозможности построения треугольника выбрасывается исключение.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 12
Описать класс, представляющий круг. Предусмотреть методы для создания объектов, вычисления площади круга, длины окружности и проверки попадания заданной точки внутрь круга. Описать свойства для получения состояния объекта.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 13
Описать класс для работы со строкой, позволяющей хранить только двоичное число и выполнять с ним арифметические операции. Предусмотреть инициализацию с проверкой допустимости значений. В случае недопустимых значений выбрасываются исключения. Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 14
Описать класс дробей — рациональных чисел, являющихся отношением двух целых чисел. Предусмотреть методы сложения, вычитания, умножения и деления дробей.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 15
Описать класс «файл», содержащий сведения об имени, дате создания и длине файла. Предусмотреть инициализацию с проверкой допустимости значений полей. В случае недопустимых значений полей выбрасываются исключения. Описать метод добавления информации в конец файла и свойства для получения состояния файла.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 16
Описать класс «комната», содержащий сведения о метраже, высоте потолков и количестве окон. Предусмотреть инициализацию с проверкой допустимости значений полей. В случае недопустимых значений полей выбрасываются исключения. Описать методы вычисления площади и объема комнаты и свойства для получения состояния объекта.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 17
Описать класс, представляющий нелинейное уравнение вида ах - cos(x) = 0. Описать метод, вычисляющий решение этого уравнения на заданном интервале методом деления пополам (см. раздел «Цикл с параметром for») и выбрасывающий исключение в случае отсутствия корня. Описать свойства для получения состояния объекта.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 18
Описать класс, представляющий квадратное уравнение вида ах2 + bх + с = 0. Описать метод, вычисляющий решение этого уравнения и выбрасывающий исключение в случае отсутствия корней. Описать свойства для получения состояния объекта.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 19
Описать класс «процессор», содержащий сведения о марке, тактовой частоте, объеме кэша и стоимости. Предусмотреть инициализацию с проверкой допустимости значений полей. В случае недопустимых значений полей выбрасываются исключения. Описать свойства для получения состояния объекта.
Описать класс «материнская плата», включающий класс «процессор» и объем установленной оперативной памяти. Предусмотреть инициализацию с проверкой допустимости значений поля объема памяти. В случае недопустимых значений поля выбрасывается исключение. Описать свойства для получения состояния объекта.
Написать программу, демонстрирующую все разработанные элементы классов.
Вариант 20
Описать класс «цветная точка». Для точки задаются координаты и цвет. Цвет описывается с помощью трех составляющих (красный, зеленый, синий). Предусмотреть различные методы инициализации объекта с проверкой допустимости значений. Допустимым диапазоном для каждой составляющей является [0, 255]. В случае недопустимых значений полей выбрасываются исключения. Описать свойства для получения состояния объекта и метод изменения цвета.
Написать программу, демонстрирующую все разработанные элементы класса.
Лабораторная работа 5. Одномерные массивы
Теоретический материал: глава 6, раздел «Массивы».
Вариант 1
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ сумму отрицательных элементов массива;
□ произведение элементов массива, расположенных между максимальным и минимальным элементами.
Упорядочить элементы массива по возрастанию.
Вариант 2
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ сумму положительных элементов массива;
□ произведение элементов массива, расположенных между максимальным по модулю и минимальным по модулю элементами.
Упорядочить элементы массива по убыванию.
Вариант 3
В одномерном массиве, состоящем из п целочисленных элементов, вычислить:
□ произведение элементов массива с четными номерами;
□ сумму элементов массива, расположенных между первым и последним нулевыми элементами.
Преобразовать массив таким образом, чтобы сначала располагались все положительные элементы, а потом — все отрицательные (элементы, равные нулю, считать положительными).
Вариант 4
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ сумму элементов массива с нечетными номерами;
□ сумму элементов массива, расположенных между первым и последним отрицательными элементами.
Сжать массив, удалив из него все элементы, модуль которых не превышает единицу. Освободившиеся в конце массива элементы заполнить нулями.
Вариант 5
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ максимальный элемент массива;
□ сумму элементов массива, расположенных до последнего положительного элемента.
Сжать массив, удалив из него все элементы, модуль которых находится в интервале [а, b]. Освободившиеся в конце массива элементы заполнить нулями.
Вариант 6
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ минимальный элемент массива;
□ сумму элементов массива, расположенных между первым и последним положительными элементами.
Преобразовать массив таким образом, чтобы сначала располагались все элементы, равные нулю, а потом — все остальные.
Вариант 7
В одномерном массиве, состоящем из п целочисленных элементов, вычислить:
□ номер максимального элемента массива;
□ произведение элементов массива, расположенных между первым и вторым нулевыми элементами.
Преобразовать массив таким образом, чтобы в первой его половине располагались элементы, стоявшие в нечетных позициях, а во второй половине — элементы, стоявшие в четных позициях.
Вариант 8
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ номер минимального элемента массива;
□ сумму элементов массива, расположенных между первым и вторым отрицательными элементами.
Преобразовать массив таким образом, чтобы сначала располагались все элементы, модуль которых не превышает единицу, а потом — все остальные.
Вариант 9
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ максимальный по модулю элемент массива;
□ сумму элементов массива, расположенных между первым и вторым положительными элементами.
Преобразовать массив таким образом, чтобы элементы, равные нулю, располагались после всех остальных.
Вариант 10
В одномерном массиве, состоящем из п целочисленных элементов, вычислить:
□ минимальный по модулю элемент массива;
□ сумму модулей элементов массива, расположенных после первого элемента, равного нулю.
Преобразовать массив таким образом, чтобы в первой его половине располагались элементы, стоявшие в четных позициях, а во второй половине — элементы, стоявшие в нечетных позициях.
Вариант 11
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ номер минимального по модулю элемента массива;
□ сумму модулей элементов массива, расположенных после первого отрицательного элемента.
Сжать массив, удалив из него все элементы, величина которых находится в интервале [а, b]. Освободившиеся в конце массива элементы заполнить нулями.
Вариант 12
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ номер максимального по модулю элемента массива;
□ сумму элементов массива, расположенных после первого положительного элемента.
Преобразовать массив таким образом, чтобы сначала располагались все элементы, целая часть которых лежит в интервале [а, b], а потом — все остальные.
Вариант 13
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ количество элементов массива, лежащих в диапазоне от А до В;
□ сумму элементов массива, расположенных после максимального элемента. Упорядочить элементы массива по убыванию модулей.
Вариант 14
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ количество элементов массива, равных нулю;
□ сумму элементов массива, расположенных после минимального элемента. Упорядочить элементы массива по возрастанию модулей.
Вариант 15
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ количество элементов массива, больших С;
□ произведение элементов массива, расположенных после максимального по модулю элемента.
Преобразовать массив таким образом, чтобы сначала располагались все отрицательные элементы, а потом — все положительные (элементы, равные нулю, считать положительными).
Вариант 16
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ количество отрицательных элементов массива;
□ сумму модулей элементов массива, расположенных после минимального по модулю элемента.
Заменить все отрицательные элементы массива их квадратами и упорядочить элементы массива по возрастанию.
Вариант 17
В одномерном массиве, состоящем из п целочисленных элементов, вычислить:
□ количество положительных элементов массива;
□ сумму элементов массива, расположенных после последнего элемента, равного нулю.
Преобразовать массив таким образом, чтобы сначала располагались все элементы, целая часть которых не превышает единицу, а потом — все остальные.
Вариант 18
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ количество элементов массива, меньших С;
□ сумму целых частей элементов массива, расположенных после последнего отрицательного элемента.
Преобразовать массив таким образом, чтобы сначала располагались все элементы, отличающиеся от максимального не более чем на 20%, а потом — все остальные.
Вариант 19
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ произведение отрицательных элементов массива;
□ сумму положительных элементов массива, расположенных до максимального элемента.
Изменить порядок следования элементов в массиве на обратный.
Вариант 20
В одномерном массиве, состоящем из п вещественных элементов, вычислить:
□ произведение положительных элементов массива;
□ сумму элементов массива, расположенных до минимального элемента.
Упорядочить по возрастанию отдельно элементы, стоящие на четных местах, и элементы, стоящие на нечетных местах.
Лабораторная работа 6. Двумерные массивы
Теоретический материал: глава 6, раздел «Массивы».
Вариант 1
Дана целочисленная прямоугольная матрица. Определить:
□ количество строк, не содержащих ни одного нулевого элемента;
□ максимальное из чисел, встречающихся в заданной матрице более одного раза.
Вариант 2
Дана целочисленная прямоугольная матрица. Определить количество столбцов, не содержащих ни одного нулевого элемента.
Характеристикой строки целочисленной матрицы назовем сумму ее положительных четных элементов. Переставляя строки заданной матрицы, расположить их в соответствии с ростом характеристик.
Вариант 3
Дана целочисленная прямоугольная матрица. Определить:
□ количество столбцов, содержащих хотя бы один нулевой элемент;
□ номер строки, в которой находится самая длинная серия одинаковых элементов.
Вариант 4
Дана целочисленная квадратная матрица. Определить:
□ произведение элементов в тех строках, которые не содержат отрицательных элементов;
□ максимум среди сумм элементов диагоналей, параллельных главной диагонали матрицы.
Вариант 5
Дана целочисленная квадратная матрица. Определить:
□ сумму элементов в тех столбцах, которые не содержат отрицательных элементов;
□ минимум среди сумм модулей элементов диагоналей, параллельных побочной диагонали матрицы.
Вариант 6
Дана целочисленная прямоугольная матрица. Определить:
□ сумму элементов в тех строках, которые содержат хотя бы один отрицательный элемент;
□номера строк и столбцов всех седловых точек матрицы.
ПРИМЕЧАНИЕ_______________________________________________________________
Матрица А имеет седловую точку Аij если Aij является минимальным элементом в i-й строке и максимальным — в j-м столбце.
Вариант 7
Для заданной матрицы размером 8x8 найти такие k, при которых k-я строка матрицы совпадает с k-u столбцом.
Найти сумму элементов в тех строках, которые содержат хотя бы один отрицательный элемент.
Вариант 8
Характеристикой столбца целочисленной матрицы назовем сумму модулей его отрицательных нечетных элементов. Переставляя столбцы заданной матрицы, расположить их в соответствии с ростом характеристик.
Найти сумму элементов в тех столбцах, которые содержат хотя бы один отрицательный элемент.
Вариант 9
Соседями элемента Аij в матрице назовем элементы Аkl, где i- 1≤k≤i+1, j - 1 ≤ / ≤ j + 1, (k, I) ≠ (i,j). Операция сглаживания матрицы дает новую матрицу того же размера, каждый элемент которой получается как среднее арифметическое имеющихся соседей соответствующего элемента исходной матрицы. Построить результат сглаживания заданной вещественной матрицы размером 10 х 10.
В сглаженной матрице найти сумму модулей элементов, расположенных ниже главной диагонали.
Вариант 10
Элемент матрицы называется локальным минимумом, если он строго меньше всех имеющихся у него соседей (определение соседних элементов см. в варианте 9). Подсчитать количество локальных минимумов заданной матрицы размером 10 х 10.
Найти сумму модулей элементов, расположенных выше главной диагонали.
Вариант 11
Коэффициенты системы линейных уравнений заданы в виде прямоугольной матрицы. С помощью допустимых преобразований привести систему к треугольному виду. '
Найти количество строк, среднее арифметическое элементов которых меньше заданной величины.
Вариант 12
Уплотнить заданную матрицу, удаляя из нее строки и столбцы, заполненные нулями.
Найти номер первой из строк, содержащих хотя бы один положительный элемент.
Вариант 13
Осуществить циклический сдвиг элементов прямоугольной матрицы на п элементов вправо или вниз (в зависимости от введенного режима), п может быть больше количества элементов в строке или столбце.
Вариант 14
Осуществить циклический сдвиг элементов квадратной матрицы размером М х N вправо на k элементов таким образом: элементы первой строки сдвигаются в последний столбец сверху вниз, из него — в последнюю строку справа налево, из нее — в первый столбец снизу вверх, из него — в первую строку; для остальных элементов — аналогично.
Вариант 15
Дана целочисленная прямоугольная матрица. Определить номер первого из столбцов, содержащих хотя бы один нулевой элемент.
Характеристикой строки целочисленной матрицы назовем сумму ее отрицательных четных элементов. Переставляя строки заданной матрицы, расположить их в соответствии
Вариант 16
Упорядочить строки целочисленной прямоугольной матрицы по возрастанию количества одинаковых элементов в каждой строке.
Найти номер первого из столбцов, не содержащих ни одного отрицательного элемента.
Вариант 17
Путем перестановки элементов квадратной вещественной матрицы добиться того, чтобы ее максимальный элемент находился в левом верхнем углу, следующий по величине — в позиции (2, 2), следующий по величине — в позиции (3, 3) и т. д., заполнив таким образом всю главную диагональ.
Найти номер первой из строк, не содержащих ни одного положительного элемента.
Вариант 18
Дана целочисленная прямоугольная матрица. Определить:
□ количество строк, содержащих хотя бы один нулевой элемент;
□ номер столбца, в котором находится самая длинная серия одинаковых элементов.
Вариант 19
Дана целочисленная квадратная матрица. Определить:
□ сумму элементов в тех строках, которые не содержат отрицательных элементов;
□ минимум среди сумм элементов диагоналей, параллельных главной диагонали матрицы.
Вариант 20
Дана целочисленная прямоугольная матрица. Определить:
□ количество отрицательных элементов в тех строках, которые содержат хотя бы один нулевой элемент;
□ номера строк и столбцов всех седловых точек матрицы.
ПРИМЕЧАНИЕ_______________________________________________________________
Матрица А имеет cедловую точку Аij, если Аij является минимальным элементом в i-й строке и максимальным — в j-м столбце.
_____________________________________________________________________________
Теоретический материал: глава 6, раздел «Символы и строки».
Вариант 1
Написать программу, которая считывает из текстового файла три предложения и выводит их в обратном порядке.
Вариант 2
Написать программу, которая считывает текст из файла и выводит на экран только предложения, содержащие введенное с клавиатуры слово.
Вариант 3
Написать программу, которая считывает текст из файла и выводит на экран только строки, содержащие двузначные числа.
Вариант 4
Написать программу, которая считывает английский текст из файла и выводит на экран слова, начинающиеся с гласных букв.
Вариант 5
Написать программу, которая считывает текст из файла и выводит его на экран, меняя местами каждые два соседних слова.
Вариант 6
Написать программу, которая считывает текст из файла и выводит на экран только предложения, не содержащие запятых.
Вариант 7
Написать программу, которая считывает текст из файла и определяет, сколько в нем слов, состоящих не более чем из четырех букв.
Вариант 8
Написать программу, которая считывает текст из файла и выводит на экран только цитаты, то есть предложения, заключенные в кавычки.
Вариант 9
Написать программу, которая считывает текст из файла и выводит на экран только предложения, состоящие из заданного количества слов.
Вариант 10
Написать программу, которая считывает английский текст из файла и выводит на экран слова текста, начинающиеся и оканчивающиеся на гласные буквы.
Вариант 11
Написать программу, которая считывает текст из файла и выводит на экран только строки, не содержащие двузначных чисел.
Вариант 12
Написать программу, которая считывает текст из файла и выводит на экран только предложения, начинающиеся с тире, перед которым могут находиться только пробельные символы.
Вариант 13
Написать программу, которая считывает английский текст из файла и выводит его на экран, заменив прописной каждую первую букву слов, начинающихся с гласной буквы.
Вариант 14
Написать программу, которая считывает текст из файла и выводит его на экран, заменив цифры от 0 до 9 словами «ноль», «один», ..., «девять», начиная каждое предложение с новой строки.
Вариант 15
Написать программу, которая считывает текст из файла, находит самое длинное слово и определяет, сколько раз оно встретилось в тексте.
Вариант 16
Написать программу, которая считывает текст из файла и выводит на экран сначала вопросительные, а затем восклицательные предложения.
Вариант 17
Написать программу, которая считывает текст из файла и выводит его на экран, после каждого предложения добавляя, сколько раз встретилось в нем введенное с клавиатуры слово.
Вариант 18
Написать программу, которая считывает текст из файла и выводит на экран все его предложения в обратном порядке.
Вариант 19
Написать программу, которая считывает текст из файла и выводит на экран сначала предложения, начинающиеся с однобуквенных слов, а затем все остальные.
Вариант 20
Написать программу, которая считывает текст из файла и выводит на экран предложения, содержащие максимальное количество знаков пунктуации.
Лабораторная работа 8. Классы и операции
Теоретический материал: глава 7.
Каждый разрабатываемый класс должен, как правило, содержать следующие элементы: скрытые поля, конструкторы с параметрами и без параметров, методы; свойства, индексаторы; перегруженные операции. Функциональные элементы класса должны обеспечивать непротиворечивый, полный, минимальный и удобный интерфейс класса. При возникновении ошибок должны выбрасываться исключения.
В программе должна выполняться проверка всех разработанных "элементов класса.
Вариант 1
Описать класс для работы с одномерным массивом целых чисел (вектором). Обеспечить следующие возможности:
□ задание произвольных целых границ индексов при создании объекта;
□ обращение к отдельному элементу массива с контролем выхода за пределы массива;
□ выполнение операций поэлементного сложения и вычитания массивов с одинаковыми границами индексов;
□ выполнение операций умножения и деления всех элементов массива на скаляр;
□ вывод на экран элемента массива по заданному индексу и всего массива. Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 2
Описать класс для работы с одномерным массивом строк фиксированной длины. Обеспечить следующие возможности:
□ задание произвольных целых границ индексов при создании объекта;
□ обращение к отдельной строке массива по индексу с контролем выхода за пределы массива;
□ выполнение операций поэлементного сцепления двух массивов с образованием нового массива;
□ выполнение операций слияния двух массивов с исключением повторяющихся элементов;
□ вывод на экран элемента массива по заданному индексу и всего массива. Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 3
Описать класс многочленов от одной переменной, задаваемых степенью многочлена и массивом коэффициентов. Обеспечить следующие возможности:
□ вычисление значения многочлена для заданного аргумента;
□ операции сложения, вычитания и умножения многочленов с получением нового объекта-многочлена;
□ получение коэффициента, заданного по индексу;
□ вывод на экран описания многочлена.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 4
Описать класс, обеспечивающий представление матрицы произвольного размера с возможностью изменения числа строк и столбцов, вывода на экран подматрицы любого размера и всей матрицы, доступа по индексам к элементу матрицы. Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 5
Описать класс для работы с восьмеричным числом, хранящимся в виде строки символов. Реализовать конструкторы, свойства, методы и следующие операции:
□ операции присваивания, реализующие значимую семантику;
□ операции сравнения;
□ преобразование в десятичное число;
□ форматный вывод;
□ доступ к заданной цифре числа по индексу.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 6
Описать класс «домашняя библиотека». Предусмотреть возможность работы с произвольным числом книг, поиска книги по какому-либо признаку (по авто-ру, по году издания или категории), добавления книг в библиотеку, удаления книг из нее, доступа к книге по номеру.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 7
Описать класс «записная книжка». Предусмотреть возможность работы с произвольным числом записей, поиска записи по какому-либо признаку (например, по фамилии, дате рождения или номеру телефона), добавления и удаления записей, сортировки по фамилии и доступа к записи по номеру.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 8
Описать класс «студенческая группа». Предусмотреть возможность работы с переменным числом студентов, поиска студента по какому-либо признаку (например, по фамилии, имени, дате рождения), добавления и удаления записей, сортировки по разным полям, доступа к записи по номеру.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 9
Описать класс, реализующий тип данных «вещественная матрица» и работу с ними. Класс должен реализовывать следующие операции над матрицами:
□ сложение, вычитание (как с другой матрицей, так и с числом);
□ комбинированные операции присваивания (+=, -=);
□ операции сравнения на равенство/неравенство;
□ операции вычисления обратной и транспонированной матрицы;
□ доступ к элементу по индексам.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 10
Описать класс, реализующий тип данных «вещественная матрица» и работу с ними. Класс должен реализовывать следующие операции над матрицами:
□ умножение, деление (как на другую матрицу, так и на число);
□ комбинированные операции присваивания (*=, /=);
□ операцию возведения в степень;
□ методы вычисления детерминанта и нормы;
□ доступ к элементу по индексам.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 11
Описать класс, реализующий тип данных «вещественная матрица» и работу с ними. Класс должен реализовывать следующие операции над матрицами:
□ методы, реализующие проверку типа матрицы (квадратная, диагональная, нулевая, единичная, симметричная, верхняя треугольная, нижняя треугольная);
□ операции сравнения на равенство/неравенство;
□ доступ к элементу по индексам.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 12
Описать класс «множество», позволяющий выполнять основные операции: добавление и удаление элемента, пересечение, объединение и разность множеств.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 13
Описать класс «предметный указатель». Каждый компонент указателя содержит слово и номера страниц, на которых это слово встречается. Количество номеров страниц, относящихся к одному слову, от одного до десяти. Предусмотреть возможность формирования указателя с клавиатуры и из файла, вывода указателя, вывода номеров страниц для заданного слова, удаления элемента из указателя.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 14
Описать класс «автостоянка» для хранения сведений об автомобилях. Для каждого автомобиля записываются госномер, цвет, фамилия владельца и признак присутствия на стоянке. Обеспечить возможность поиска автомобиля по разным критериям, вывода списка присутствующих и отсутствующих на стоянке автомобилей, доступа к имеющимся сведениям по номеру места.
Написать программу, демонстрирующую все разработанные элементы класса.
Вариант 15
Описать класс «колода карт», включающий закрытый массив элементов класса «карта». В карте хранятся масть и номер. Обеспечить возможность вывода карты по номеру, вывода всех карт, перемешивания колоды и выдачи всех карт из колоды поодиночке и по 6 штук в случайном порядке.
Написать программу, демонстрирующую все разработанные элементы классов.
Вариант 16
Описать класс «поезд», содержащий следующие закрытые поля:
□ название пункта назначения;
□ номер поезда (может содержать буквы и цифры);
□ время отправления.
Предусмотреть свойства для получения состояния объекта. Описать класс «вокзал», содержащий закрытый массив поездов. Обеспечить следующие возможности:
□ вывод информации о поезде по номеру с помощью индекса;
□ вывод информации о поездах, отправляющихся после введенного с клавиатуры времени;
□ перегруженную операцию сравнения, выполняющую сравнение времени отправления двух поездов;
□ вывод информации о поездах, отправляющихся в заданный пункт назначения. Информация должна быть отсортирована по времени отправления. Написать программу, демонстрирующую все разработанные элементы классов.
Вариант 17
Описать класс «товар», содержащий следующие закрытые поля:
□ название товара;
□ название магазина, в котором продается товар;
□ стоимость товара в рублях.
Предусмотреть свойства для получения состояния объекта. Описать класс «склад», содержащий закрытый массив товаров. Обеспечить следующие возможности:
□ вывод информации о товаре по номеру с помощью индекса;
□ вывод на экран информации о товаре, название которого введено с клавиатуры; если таких товаров нет, выдать соответствующее сообщение;
□ сортировку товаров по названию магазина, по наименованию и по цене;
□ перегруженную операцию сложения товаров, выполняющую сложение их цен. Написать программу, демонстрирующую все разработанные элементы классов.
Вариант 18
Описать класс «самолет», содержащий следующие закрытые поля:
□ название пункта назначения;
□ шестизначный номер рейса;
□ время отправления.
Предусмотреть свойства для получения состояния объекта.
Описать класс «аэропорт», содержащий закрытый массив самолетов. Обеспечить
следующие возможности:
□ вывод информации о самолете по номеру рейса с помощью индекса;
□ вывод информации о самолетах, отправляющихся в течение часа после введенного с клавиатуры времени;
□ вывод информации о самолетах, отправляющихся в заданный пункт назначения;
□ перегруженную операцию сравнения, выполняющую сравнение времени отправления двух самолетов.
Информация должна быть о сортирована по времени отправления. Написать программу, демонстрирующую все разработанные элементы классов.
Вариант 19
Описать класс «запись», содержащий следующие закрытые поля:
□ фамилия, имя;
□ номер телефона;
□ дата рождения (массив из трех чисел). Предусмотреть свойства для получения состояния объекта.
Описать класс «записная книжка», содержащий закрытый массив записей. Обеспечить следующие возможности:
□ вывод на экран информации о человеке, номер телефона которого введен с клавиатуры; если такого нет, выдать на дисплей соответствующее сообщение;
□ поиск людей, день рождения которых сегодня или в заданный день;
□ поиск людей, день рождения которых будет на следующей неделе;
□ поиск людей, номер телефона которых начинается на три заданных цифры. Написать программу, демонстрирующую все разработанные элементы классов.
Вариант 20
Описать класс «англо-русский словарь», обеспечивающий возможность хранения нескольких вариантов перевода для каждого слова. Реализовать доступ по строковому индексу — английскому слову. Обеспечить возможность вывода всех значений слов по заданному префиксу.
Лабораторная работа 9. Наследование
Теоретический материал: глава 8.
В программах требуется описать базовый класс (возможно, абстрактный), в котором с помощью виртуальных или абстрактных методов и свойств задается интерфейс для производных классов. Целью лабораторной работы является максимальное использование наследования, даже если для конкретной задачи оно не дает выигрыша в объеме программы. Во всех классах следует переопределить метод Equals, чтобы обеспечить сравнение значений, а не ссылок.
Функция Main должна содержать массив из элементов базового класса, заполненный ссылками на производные классы. В этой функции должно демонстрироваться использование всех разработанных элементов классов.
Вариант 1
Создать класс Point (точка). На его основе создать классы ColoredPoint и Line (линия). На основе класса Line создать классы ColoredLine и PolyLine (многоугольник). В классах описать следующие элементы:
□ конструкторы с параметрами и конструкторы по умолчанию;
□ свойства для установки и получения значений всех координат, а также для изменения цвета и получения текущего цвета;
Q для линий — методы изменения угла поворота линий относительно первой точки;
□ для многоугольника — метод масштабирования.
Вариант 2
Создать абстрактный класс Vehicle (транспортное средство). На его основе реализовать классы Plane (самолет), Саг (автомобиль) и Ship (корабль). Классы должны иметь возможность задавать и получать координаты и параметры средств передвижения (цена, скорость, год выпуска и т. п.) с помощью свойств. Для самолета должна быть определена высота, для самолета и корабля — количество пассажиров, для корабля —* порт приписки. Динамические характеристики задать с помощью методов.
Вариант 3
Описать базовый класс Строка. Обязательные поля класса:
□ поле для хранения символов строки;
□ значение типа word для хранения длины строки в байтах. Реализовать обязательные методы следующего назначения:
□ конструктор без параметров;
□ конструктор, принимающий в качестве параметра строковый литерал;
□ конструктор, принимающий в качестве параметра символ;
□ метод получения длины строки;
О метод очистки строки (сделать строку пустой).
Описать производный от Строка класс Комплексное _число.
Строки данного класса состоят из двух полей, разделенных символом i.
Первое поле задает значение действительной части числа, второе — значение мнимой. Каждое из полей может содержать только символы десятичных цифр и символы - и +, задающие знак числа. Символы - или + могут находиться только в первой позиции числа, причем символ + может отсутствовать, в этом случае число считается положительным. Если в составе инициализирующей строки будут встречены любые символы, отличные от допустимых, класс Комплексное _число принимает нулевое значение. Примеры строк:
33112
-7i100
+51 - 21
Для класса Комплексное_число определить следующие методы:
□ проверка на равенство;
□ сложение чисел;
□ умножение чисел.
Вариант 4
Описать базовый класс Строка в соответствии с вариантом 3. Описать производный от Строка класс Десятичная_строка.
Строки данного класса могут содержать только символы десятичных цифр и символы - и +, задающие знак числа. Символы - или + могут находиться только в первой позиции числа, причем символ + может отсутствовать, в этом случае число считается положительным. Если в составе инициализирующей строки будут встречены любые символы, отличные от допустимых, класс Десятичная_строка принимает нулевое значение. Содержимое данных строк рассматривается как десятичное число.
Для класса определить следующие методы:
□ конструктор, принимающий в качестве параметра число;
□ арифметическая разность строк;
□ проверка на больше (по значению);
□ проверка на меньше (по значению).
Вариант 5
Описать базовый класс Строка в соответствии с вариантом 3. Описать производный от Строка класс Битовая_ строка.
Строки данного класса могут содержать только символы ' 0' или ' 1'. Если в составе инициализирующей строки будут встречены любые символы, отличные от допустимых, класс Битовая_ строка принимает нулевое значение. Содержимое данных строк рассматривается как двоичное число. Отрицательные числа хранятся в дополнительном коде.
Для класса Битовая _строка определить следующие методы:
□ конструктор, принимающий в качестве параметра строковый литерал;
□ деструктор;
□ изменение знака на противоположный (перевод числа в дополнительный код);
□ присваивание;
□ вычисление арифметической суммы строк;
□ проверка на равенство.
В случае необходимости более короткая битовая строка расширяется влево знаковым разрядом.
Вариант 6
1. Описать базовый класс Элемент. Закрытые поля:
О имя элемента (строка символов);
О количество входов элемента;
О количество выходов элемента.
Методы:
О конструктор класса без параметров;
О конструктор, задающий имя и устанавливающий равным 1 количество входов и выходов;
О конструктор, задающий значения всех полей элемента.
Свойства:
О имя элемента (только чтение);
О количество входов элемента;
О количество выходов элемента.
2. На основе класса Элемент описать производный класс Комбинационный, представляющий собой комбинационный элемент (двоичный вентиль), который может иметь несколько входов и один выход.
Поле — массив значений входов.
Методы:
О конструкторы;
О метод, задающий значение на входах экземпляра класса;
О метод, позволяющий опрашивать состояние отдельного входа экземпляра класса;
О метод, вычисляющий значение выхода (по варианту задания).
3. На основе класса Элемент описать производный класс Память, представляющий собой триггер. Триггер имеет входы, соответствующие типу триггера (см. далее вариант задания), и входы установки и сброса. Все триггеры считаются синхронными, сам синхровход в состав триггера не включается.
Поля:
О массив значений входов объекта класса, в массиве учитываются все входы (управляющие и информационные);
О состояние на прямом выходе триггера;
О состояние на инверсном выходе триггера.
Методы:
О конструктор (по умолчанию сбрасывает экземпляр класса);
О конструктор копирования;
О метод, задающий значение на входах экземпляра класса;
О методы, позволяющие опрашивать состояния отдельного входа экземпляра класса;
О метод, вычисляющий состояние экземпляра класса (по варианту задания) зависимости от текущего состояния и значений на входах;
О метод, переопределяющий операцию = = для экземпляров класса.
4. Создать класс Регистр, используя класс Память как вложенный класс. Поля:
О состояние входа «Сброс» — один для экземпляра класса;
О состояние входа «Установка» — один для экземпляра класса;
О массив типа Память заданной в варианте размерности;
О массив (массивы), содержащий значения на соответствующих входах элементов массива типа Память.
Методы:
О метод, задающий значение на входах экземпляра класса;
О метод, позволяющий опрашивать состояние отдельного выхода экземпляра класса;
О метод, вычисляющий значение нового состояния экземпляра класса.
Все поля классов Элемент, Комбинационный и Память должны быть описаны с ключевым словом private.
В задании перечислены только обязательные члены и методы класса. Можно задавать дополнительные члены и методы, если они не отменяют обязательные и обеспечивают дополнительные удобства при работе с данными классами, например, описать функции вычисления выхода/состояния как виртуальные.
5. Для проверки функционирования созданных классов написать программу, использующую эти классы. В программе должны быть продемонстрированы все свойства созданных классов. .
Конкретный тип комбинационного элемента, тип триггера и разрядность регистра выбираются в соответствии с вариантом задания:
Лабораторная работа 10. Структуры
Теоретический материал: глава 9.
Вариант 1
Описать структуру с именем STUDENT, содержащую следующие поля:
□ фамилия и инициалы;
□ номер группы;
□ успеваемость (массив из пяти элементов). Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из десяти структур типа STUDENT (записи должны быть упорядочены по возрастанию номера группы);
□ вывод на экран фамилий и номеров групп для всех студентов, включенных в массив, если средний балл студента больше 4,0 (если таких студентов нет, вывести соответствующее сообщение).
Вариант 2
Описать структуру с именем STUDENT, содержащую следующие поля:
□ фамилия и инициалы;
□ номер группы;
□ успеваемость (массив из пяти элементов).
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из десяти структур типа STUDENT
(записи должны быть упорядочены по возрастанию среднего балла);
□ вывод на экран фамилий и номеров групп для всех студентов, имеющих оценки 4 и 5 (если таких студентов нет, вывести соответствующее сообщение).
Вариант 3
Описать структуру с именем STUDENT, содержащую следующие поля:
□ фамилия и инициалы;
□ номер группы;
□ успеваемость (массив из пяти элементов). Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из десяти структур типа STUDENT (записи должны быть упорядочены по алфавиту);
□ вывод на экран фамилий и номеров групп для всех студентов, имеющих хотя бы одну оценку 2 (если таких студентов нет, вывести соответствующее сообщение).
Вариант 4
Описать структуру с именем AEROFLOT, содержащую следующие поля:
□ название пункта назначения рейса;
□ номер рейса;
□ тип самолета.
Написать программу, выполняющую следующие действия:
О ввод с клавиатуры данных в массив, состоящий из семи элементов типа AEROFLOT (записи должны быть упорядочены по возрастанию номера рейса);
□ вывод на экран номеров рейсов и типов самолетов, вылетающих в пункт назначения, название которого совпало с названием, введенным с клавиатуры (если таких рейсов нет, вывести соответствующее сообщение).
Вариант 5
Описать структуру с именем AEROFLOT, содержащую следующие поля:
□ название пункта назначения рейса;
□ номер рейса;
□ тип самолета.
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из семи элементов типа AEROFLOT (записи должны быть размещены в алфавитном порядке по названиям пунктов назначения);
□ вывод на экран пунктов назначения и номеров рейсов, обслуживаемых самолетом, тип которого введен с клавиатуры (если таких рейсов нет, вывести со-ответс|вующее сообщение).
Вариант 6
Описать структуру с именем WORKER, содержащую следующие поля:
□ фамилия и инициалы работника;
□ название занимаемой должности;
□ год поступления на работу.
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из десяти структур типа WORKER (записи должны быть упорядочены по алфавиту);
□ вывод на экран фамилий работников, стаж работы которых превышает значение, введенное с клавиатуры (если таких работников нет, вывести соответствующее сообщение).
Вариант 7
Описать структуру с именем TRAIN, содержащую следующие поля:
□ название пункта назначения; О номер поезда;
□ время отправления.
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из восьми элементов типа TRAIN (записи должны быть размещены в алфавитном порядке по названиям пунктов назначения);
□ вывод на экран информации о поездах, отправляющихся после введенного с клавиатуры времени (если таких поездов нет, вывести соответствующее сообщение).
Вариант 8
Описать структуру с именем TRAIN, содержащую следующие поля:
□ название пункта назначения;
□ номер поезда;
□ время отправления.
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из шести элементов типа TRAIN (записи должны быть упорядочены по времени отправления поезда);
□ вывод на экран информации о поездах, направляющихся в пункт, название которого введено с клавиатуры (если таких поездов нет, вывести соответствующее сообщение).
Вариант 9
Описать структуру с именем TRAIN, содержащую следующие поля:
□ название пункта назначения;
□ номер поезда;
□ время отправления.
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из восьми элементов типа TRAIN (записи должны быть упорядочены по номерам поездов);
□ вывод на экран информации о поезде, номер которого введен с клавиатуры (если таких поездов нет, вывести соответствующее сообщение).
Вариант 10
Описать структуру с именем MARSH, содержащую следующие поля:
□ название начального пункта маршрута;
□ название конечного пункта маршрута;
□ номер маршрута.
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из восьми элементов типа MARSH (записи должны быть упорядочены по номерам маршрутов);
□ вывод на экран информации о маршруте, номер которого введен с клавиатуры (если таких маршрутов нет, вывести соответствующее сообщение).
Вариант 11
Описать структуру с именем MARSH, содержащую следующие поля:
□ название начального пункта маршрута;
□ название конечного пункта маршрута;
□ номер маршрута.
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из восьми элементов типа MARSH (записи должны быть упорядочены по номерам маршрутов);
□ вывод на экран информации о маршрутах, которые начинаются или оканчиваются в пункте, название которого введено с клавиатуры (если таких маршрутов нет, вывести соответствующее сообщение).
Вариант 12
Описать структуру с именем NOTE, содержащую следующие поля:
□ фамилия, имя;
□ номер телефона;
□ дата рождения (массив из трех чисел).
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из восьми элементов типа NOTE (записи должны быть упорядочены по дате рождения);
□ вывод на экран информации о человеке, номер телефона которого введен с клавиатуры (если такого нет, вывести соответствующее сообщение).
Вариант 13
Описать структуру с именем NOTE, содержащую следующие поля:
□ фамилия, имя;
□ номер телефона;
□ дата рождения (массив из трех чисел).
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из восьми элементов типа NOTE (записи должны быть размещены по алфавиту);
□ вывод на экран информации о людях, чьи дни рождения приходятся на месяц, значение которого введено с клавиатуры (если таких нет, вывести соответствующее сообщение).
Вариант 14
Описать структуру с именем NOTE, содержащую следующие поля:
□ фамилия, имя;
□ номер телефона;
□ дата рождения (массив из трех чисел).
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из восьми элементов типа NOTE (записи должны быть упорядочены по трем первым цифрам номера телефона);
□ вывод на экран информации о человеке, чья фамилия введена с клавиатуры (если такого нет, вывести соответствующее сообщение).
Вариант 15
Описать структуру с именем ZNAK, содержащую следующие поля:
□ фамилия, имя;
□ знак Зодиака;
□ дата рождения (массив из трех чисел).
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из восьми элементов типа ZNAK (записи должны быть упорядочены по дате рождения);
□ вывод на экран информации о человеке, чья фамилия введена с клавиатуры (если такого нет, вывести соответствующее сообщение).
Вариант 16
Описать структуру с именем ZNAK, содержащую следующие поля:
□ фамилия, имя;
□ знак Зодиака;
□ дата рождения (массив из трех чисел).
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из восьми элементов типа ZNAK (записи должны быть упорядочены по дате рождения);
□ вывод на экран информации о людях, родившихся под знаком, название которого введено с клавиатуры (если таких нет, вывести соответствующее сообщение).
Вариант 17
Описать структуру с именем ZNAK, содержащую следующие поля:
□ фамилия, имя; О знак Зодиака;
□ дата рождения (массив из трех чисел).
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из восьми элементов типа ZNAK (записи должны быть упорядочены по знакам Зодиака);
□ вывод на экран информации о людях, родившихся в месяц, значение которого введено с клавиатуры (если таких нет, вывести соответствующее сообщение).
Вариант 18
Описать структуру с именем PRICE, содержащую следующие поля:
□ название товара;
□ название магазина, в котором продается товар;
□ стоимость товара в рублях.
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из восьми элементов типа PRICE (записи должны быть упорядочены в алфавитном порядке по названиям товаров);
□ вывод на экран информации о товаре, название которого введено с клавиатуры (если таких товаров нет, вывести соответствующее сообщение).
Вариант 19
Описать структуру с именем PRICE, содержащую следующие поля:
□ название товара;
□ название магазина, в котором продается товар;
□ стоимость товара в рублях.
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из восьми элементов типа PRICE (записи должны быть упорядочены в алфавитном порядке по названиям магазинов);
□ вывод на экран информации о товарах, продающихся в магазине, название которого введено с клавиатуры (если такого магазина нет, вывести соответствующее сообщение).
Вариант 20
Описать структуру с именем ORDER, содержащую следующие поля:
□ расчетный счет плательщика;
□ расчетный счет получателя;
□ перечисляемая сумма в рублях.
Написать программу, выполняющую следующие действия:
□ ввод с клавиатуры данных в массив, состоящий из восьми элементов типа ORDER (записи должны быть размещены в алфавитном порядке по расчетным счетам плательщиков);
□ вывод на экран информации о сумме, снятой с расчетного счета плательщика, введенного с клавиатуры (если такого расчетного счета нет, вывести соответствующее сообщение).
Лабораторная работа 11. Интерфейсы и параметризованные коллекции
Теоретический материал: главы 9, 13.
Выполнить задания лабораторной работы 9, используя для хранения экземпляров разработанных классов стандартные параметризованные коллекции. Во всех классах реализовать интерфейс I Comparable и перегрузить операции отношения для реализации значимой семантики сравнения объектов по какому-либо полю на усмотрение студента.
Лабораторная работа 12. Создание Windows-приложений
Теоретический материал: глава 14.
Задание 1. Диалоговые окна
Общая часть задания: написать Windows-приложение, заголовок главного окна которого содержит Ф. И. О., группу и номер варианта. В программе должна быть предусмотрена обработка исключений, возникающих из-за ошибочного ввода пользователя
Вариант 1
Создать меню с командами Input, Calc и Exit.
При выборе команды Input открывается диалоговое окно, содержащее:
□ три поля типа TextBox для ввода длин трех сторон треугольника;
□ группу из двух флажков (Периметр и Площадь) типа CheckBox;
□ кнопку типа Button. Обеспечить возможность:
□ ввода длин трех сторон треугольника;
□ выбора режима с помощью флажков: подсчет периметра и/или площади треугольника.
При выборе команды Calc открывается диалоговое окно с результатами. При выборе команды Exit приложение завершается.
Вариант 2
Создать меню с командами Size, Color, Paint, Quit.
Команда Paint недоступна. При выборе команды Quit приложение завершается. При выборе команды Size открывается диалоговое окно, содержащее:
□ два поля типа TextBox для ввода длин сторон прямоугольника;
□ группу из трех флажков (Red, Green, Blue) типа CheckBox;
□ кнопку типа Button. Обеспечить возможность:
□ ввода длин сторон прямоугольника в пикселах в поля ввода;
□ выбора его цвета с помощью флажков.
После задания параметров команда Paint становится доступной.
При выборе команды Paint в главном окне приложения выводится прямоугольник заданного размера и сочетания цветов или выдается сообщение, если введенные размеры превышают размер окна.
Вариант 3
Создать меню с командами Input, Work, Exit.
При выборе команды Exit приложение завершает работу. При выборе команды Input открывается диалоговое окно, содержащее:
□ три поля ввода типа TextBox с метками Radius, Height, Density;
□ группу из двух флажков (Volume, Mass) типа CheckBox;
□ кнопку типа Button. Обеспечить возможность:
□ ввода радиуса, высоты и плотности конуса;
□ выбора режима с помощью флажков: подсчет объема и/или массы конуса. При выборе команды Work открывается окно сообщений с результатами.
Вариант 4
Создать меню с командами Input, Calc, Draw, Exit.
При выборе команды Exit приложение завершает работу. При выборе команды Input открывается диалоговое окно, содержащее:
□ поле ввода типа TextBox с меткой Radius;
□ группу из двух флажков (Square, Length) типа CheckBox;
□ кнопку типа Button. Обеспечить возможность:
□ ввода радиуса окружности;
□ выбора режима с помощью флажков: подсчет площади круга (Square) и/или длины окружности (Length).
При выборе команды Calc открывается окно сообщений с результатами. При выборе команды Draw в центре главного окна выводится круг введенного радиуса или выдается сообщение, что рисование невозможно (если диаметр превышает размеры рабочей области).
Вариант 5
Создать меню с командами Input, Calc, About.
При выборе команды About открывается окно с информацией о разработчике. При выборе команды Input открывается диалоговое окно, содержащее:
□ три поля ввода типа TextBox с метками Number 1, Number 2, Number 3;
□ группу из двух флажков (Summ, Least multiple) типа CheckBox;
□ кнопку типа Button.
Обеспечить возможность ввода трех чисел и выбора режима вычислений с помощью флажков: подсчет суммы трех чисел (Summ) и/или наименьшего общего кратного двух первых чисел (Least multiple). При выборе команды Calc открывается диалоговое окно с результатами.
Вариант 6
Создать меню с командами Input, Calc, Quit.
Команда Calc недоступна. При выборе команды Quit приложение завершается. При выборе команды Input открывается диалоговое окно, содержащее:
□ два поля ввода типа TextBox с метками Number 1, Number 2;
□ группу из трех флажков (Summa, Max divisor, Multiply) типа CheckBox;
□ кнопку типа Button. Обеспечить возможность:
□ ввода двух чисел;
□ выбора режима вычислений с помощью флажков (можно вычислять в любой комбинации такие величины, как сумма, наибольший общий делитель и произведение двух чисел).
При выборе команды Calc открывается окно сообщений с результатами.
Вариант 7
Создать меню с командами Begin, Help, About.
При выборе команды About открывается окно с информацией о разработчике. При выборе команды Begin открывается диалоговое окно, содержащее:
□ поле ввода типа TextBox с меткой input;
□ метку типа Label для вывода результата;
□ группу из трех переключателей (2, 8, 16) типа Radio Button;
□ две кнопки типа Button — Do и ОК. Обеспечить возможность:
□ ввода числа в десятичной системе в поле input;
□ выбора режима преобразования с помощью переключателей: перевод в двоичную, восьмеричную или шестнадцатеричную систему счисления.
При щелчке на кнопке Do должен появляться результат перевода.
Вариант 8
Создать меню с командами Input color, Change, Exit, Help.
При выборе команды Exit приложение завершает работу. При выборе команды Input color открывается диалоговое окно, содержащее:
□ три поля ввода типа TextBox с метками Red, Green, Blue;
□ группу из двух флажков (Left, Right) типа CheckBox;
□ кнопку типа Button.
Обеспечить возможность ввода RGB-составляющих цвета. При выборе команды Change цвет главного окна изменяется на заданный (левая, правая или обе половины окна в зависимости от установки флажков).
Вариант 9
Создать меню с командами Input size, Choose, Change, Exit.
При выборе команды Exit приложение завершает работу. Команда Change недоступна. При выборе команды Input size открывается диалоговое окно, содержащее:
Q два поля ввода типа TextBox с метками Size x, Size у;
□ кнопку типа Button.
При выбoре команды Choose открывается диалоговое окно, содержащее:
□ группу из двух переключателей (Increase, Decrease) типа Radio Button;
□ кнопку типа Button.
Обеспечить возможность ввода значений в поля Size x и Size у. Значения интерпретируются как количество пикселов, на которое надо изменить размеры главного окна (увеличить или уменьшить в зависимости от положения переключателей).
После ввода значений команда Change становится доступной. При выборе этой команды размеры главного окна увеличиваются или уменьшаются на введенное количество пикселов.
Вариант 10
Создать меню с командами Begin, Work, About.
При выборе команды About открывается окно с информацией о разработчике. При выборе команды Begin открывается диалоговое окно, содержащее:
□ поле ввода типа TextBox с меткой Input word;
□ группу из двух переключателей (Upper case, Lower case) типа Radio Button;
□ кнопку типа Button.
Обеспечить возможность ввода слова и выбора режима перевода в верхний или нижний регистр в зависимости от положения переключателей. При выборе команды Work открывается диалоговое окно с результатом перевода.
Вариант 11
Создать меню с командами Input color, Change, Clear.
При выборе команды Input color открывается диалоговое окно, содержащее:
□ группу из двух флажков (Up, Down) типа CheckBox;
□ группу из трех переключателей (Red, Green, Blue) типа RadioButton;
□ кнопку типа Button.
Обеспечить возможность:
□ выбора цвета с помощью переключателей;
□ ввода режима, определяющего, какая область закрашивается: все окно, его верхняя или нижняя половина.
При выборе команды Change цвет главного окна изменяется на заданный (верхняя, нижняя или обе половины в зависимости от введенного режима). При выборе команды Clear восстанавливается первоначальный цвет окна.
Вариант 12
Создать меню с командами Translate, Help, About, Exit.
При выборе команды Exit приложение завершает работу. При выборе команды Translate открывается диалоговое окно, содержащее:
□ поле ввода типа TextBox с меткой Binary number;
□ поле ввода типа TextBox для вывода результата (read-only);
□ группу из трех переключатели (8, 10, 16) типа RadioButton;
□ кнопку Do типа Button. Обеспечить возможность:
□ ввода числа в двоичной системе в поле Binary number;
□ выбора режима преобразования с помощью переключателей: перевод в восьмеричную, десятичную или шестнадцатеричную систему счисления.
При щелчке на кнопке Do должен появляться результат перевода.
Вариант 13
Создать меню с командами Reverse, About, Exit.
При выборе команды About открывается окно с информацией о разработчике. При выборе команды Reverse открывается диалоговое окно, содержащее:
□ поле ввода типа TextBox с меткой Input;
□ группу из двух переключателей (Upper case, Reverse) типа CheckBox;
□ кнопку OK типа Button.
Обеспечить возможность ввода фразы и выбора режима:- перевод в верхний регистр и/или изменение порядка следования символов на обратный в зависимости от состояния переключателей. Результат преобразования выводится в исходное поле ввода.
Вариант 14
Создать меню с командами Input, Show и Exit.
При выборе команды Exit приложение завершает работу. При выборе команды Input открывается диалоговое окно вида:
Обеспечивается возможность ввода координат двух точек и выбора режима с помощью флажков length и koef: подсчет длины отрезка, соединяющего эти точки, и/или углового коэффициента.
При выборе команды Show открывается окно сообщений с результатами подсчета.
Вариант 15
Создать меню с командами Input, About и Exit.
При выборе команды Exit приложение завершает работу. При выборе команды About открывается окно с информацией о разработчике. При выборе команды Input открывается диалоговое окно вида:
Обеспечивается возможность ввода суммы в рублях и перевода ее в евро и доллары по обычному или льготному курсу. Поля Euro и $ доступны только для чтения.
Вариант 16
Создать меню с командами Begin, Work, About.
При выборе команды About открывается окно с информацией о разработчике. При выборе команды Begin открывается диалоговое окно, содержащее:
□ два поля ввода типа TextBox;
□ группу из двух переключателей (First letter, All letters) типа Radio Button;
□ кнопку типа Button.
Обеспечить возможность ввода предложения и выбора режима его преобразования: либо начинать с прописной буквы каждое слово (First letter), либо перевести все буквы в верхний регистр (All letters). При выборе команды Work открывается диалоговое окно с результатом преобразования.
Вариант 17
Написать анализатор текстовых файлов, выводящий информацию о количестве слов в тексте, а также статистическую информацию о введенной пользователем букве.
Создать следующую систему меню:
□ Файл
О Загрузить текст
О Выход
□ Анализ
О Количество слов
О Повторяемость буквы
При выборе файла для загрузки использовать объект типа OpenFileDialog. При выборе команды Количество слов программа должна вывести в окно сообщений количество слов в тексте.
При выборе команды Повторяемость буквы программа предлагает пользователю ввести букву, а затем выводит количество ее повторений без учета регистра в окно сообщений.
Вариант 18
Создать редактор текстовых файлов с возможностью сохранения текста в формате HTML. Создать следующую систему меню:
□ Файл
О Загрузить текст
О Сохранить как текст
О Сохранить как HTML
□ Выход
При выборе файла для загрузки использовать объект OpenFi leDialog. При выборе файла для сохранения использовать объект SaveFiieDialog. Для редактирования текста использовать объект Memo.
При сохранении текста в формате HTML текст записывать в файл с заменой:
□ всех пробелов на символы ;
□ всех символов перевода строки на символы <BR>;
□ всех символов < на символы &11;;
□ всех символов > на символы >;
□ всех символов & на символы &атр;;
□ всех символов " (двойные кавычки) на символы ".
Вариант 19
Создать меню с командами Input, Draw, Clear.
При выборе команды Input открывается диалоговое окно, содержащее:
□ четыре поля для ввода координат двух точек;
□ группу из трех переключателей (Red, Green, Blue) типа Radio Button;
□ кнопку типа Button.
При выборе команды Draw в главное окно выводится отрезок прямой выбранного цвета с координатами концов отрезка, заданными в диалоговом окне. При выборе команды Clear отрезок стирается.
Вариант 20
Создать меню с командами Input, Change, Exit.
При выбор команды Exit приложение завершает работу. Команда Change недоступна. В центре главного окна выведен квадрат размером 100 х 100 пикселов. При выборе команды Input открывается диалоговое окно, содержащее:
□ два поля ввода типа TextBox с метками Size x, Size у;
□ группу из двух переключателей (Increase, Decrease) типа Radio Button;
□ кнопку типа Button.
Обеспечить возможность ввода значений в поля Size x и Size у. Значения интерпретируются как количество пикселов, на которое надо изменить размеры квадрата, выведенного в главное окно (увеличить или уменьшить в зависимости от положения переключателей).
После ввода значений команда Change становится доступной. При выборе этой команды размеры квадрата увеличиваются или уменьшаются на введенное количество пикселов. Если квадрат выходит за пределы рабочей области окна, выдается сообщение.
Вариант 21
Написать Windows-приложение, которое по заданным в файле исходным данным выводит информацию о компьютерах.
Создать меню с командами Choose, Show, Quit.
Команда Show недоступна. Команда Quit завершает работу приложения.
При запуске приложения из файла читаются исходные данные. Файл необходимо сформировать самостоятельно. Каждая строка файла содержит тип компьютера, цену (price) и емкость жесткого диска (hard drive).
При выборе команды Choose открывается диалоговое окно, содержащее:
□ поле типа TextBox для ввода минимальной емкости диска;
□ поле типа TextBox для ввода максимальной приемлемой цены;
□ группу из двух переключателей (Hard drive, Price) типа Radio Button;
□ OK, Cancel — кнопки типа Button.
После ввода всех данных команда меню Show становится доступной. Команда Show открывает диалоговое окно, содержащее список компьютеров, удовлетворяющий введенным ограничениям и упорядоченный по отмеченной характеристике.
Задание 2. Структуры и параметризованные коллекции
Описать структуру,, соответствующую заданиям лабораторной работы 10. Создать параметризованную коллекцию (см. раздел «Классы-прототипы») для хранения описанной структуры. Вид коллекции выбрать самостоятельно. Написать Windows-приложение для работы с этой коллекцией, позволяющее выполнять:
□ добавление элемента в коллекцию с клавиатуры;
□ считывание данных из файла;
□ запись данных в тот же или указанный файл;
□ сортировку данных по различным критериям;
□ поиск элемента по заданному полю;
□ вывод всех элементов, удовлетворяющих заданному условию;
□ удаление элемента из коллекции1.
Приложение должно содержать меню и диалоговые окна и предусматривать обработку возможных ошибок пользователя с помощью исключений.
Задание 3. Графика в Windows Вариант 1
Написать Windows-приложение, которое выполняет анимацию изображения. Создать меню с командами Show picture, Choose, Animate, Stop, Quit.
Команда Quit завершает работу приложения. При выборе команды Show picture в центре экрана рисуется объект, состоящий из нескольких графических примитивов.
При выборе команды Choose открывается диалоговое окно, содержащее:
□ поле типа TextBox с меткой Speed для ввода скорости движения объекта;
□ группу Direction из двух переключателей (Up-Down, Left-Right) типа Radi oButton для выбора направления движения;
□ кнопку типа Button.
По команде Animate объект начинает перемещаться в выбранном направлении до края окна и обратно с заданной скоростью, по команде Stop — прекращает движение.
Вариант 2
Написать Windows-приложение, которое по заданным в файле исходным данным строит график или столбиковую диаграмму.
Создать меню с командами Input data, Choose, Line, Bar, Quit.
Команды Line и Bar недоступны. Команда Quit завершает работу приложения.
При выборе команды Input data из файла читаются исходные данные (файл сформировать самостоятельно).
По команде Choose открывается диалоговое окно, содержащее:
□ список для выбора цвета графика типа TListBox;
□ группу из двух переключателей (Line, Bar) типа Radio Button;
□ кнопку типа Button.
Обеспечить возможность ввода цвета и выбора режима: построение графика (Line) или столбиковой диаграммы (Ваг). После указания параметров становится доступной соответствующая команда меню.
По команде Line или Ваг в главном окне приложения выбранным цветом строится график или диаграмма. Окно должно содержать заголовок графика или диаграммы, наименование и градацию осей. Изображение должно занимать все окно и масштабироваться при изменении размеров окна.
Вариант 3
Написать Windows-приложение, которое строит графики четырех заданных функций.
Создать меню с командами Chart, Build, Clear, About, Quit.
Команда Quit завершает работу приложения. При выборе команды About открывается окно с информацией о разработчике.
Команда Chart открывает диалоговое окно, содержащее: О список для выбора цвета графика типа TListBox;
□ список для выбора типа графика типа TListBox, содержащий четыре пункта: sin(x), sin(x+π/4), cos(x), cos(x- π/4);
□ кнопку типа Button.
Обеспечить возможность выбора цвета и вида графика. После щелчка на кнопке ОК в главном окне приложения строится график выбранной функции на интервале от - π//2 до + π/2. Окно должно содержать заголовок графика, наименование и градацию осей. Изображение должно занимать все окно и масштабироваться при изменении размеров окна.
Команда Clear очищает окно.
Вариант 4
Написать Windows-приложение — графическую иллюстрацию сортировки методом выбора.
Создать меню с командами File, Animate, About, Exit.
Команда Animate недоступна. Команда Exit завершает работу приложения. Команда About открывает окно с информацией о разработчике. Для выбора файла исходных данных (команда File) использовать объект класса OpenFileDialog.
Из выбранного файла читаются исходные данные для сортировки (сформировать самостоятельно не менее трех файлов различной длины с данными целого типа).
После чтения данных становится доступной команда Animate.
При выборе команды Animate в главном окне приложения отображается процесс сортировки в виде столбиковой диаграммы. Каждый элемент представляется столбиком соответствующего размера. На каждом шаге алгоритма два элемента меняются местами. Окно должно содержать заголовок. Изображение должно занимать все окно.
Вариант 5
Написать Windows-приложение — графическую иллюстрацию аппроксимации методом наименьших квадратов зависимости
у = а∙х + b∙х + с ∙ log 2 х.
Создать меню с командами Open, Coefficients, Show, About, Exit.
Команда Exit завершает работу приложения. Команда About открывает окно с информацией о разработчике. Для выбора файла исходных данных (команда Open) использовать объект OpenFileDialog. Исходные данные для аппроксимации — массивы экспериментальных значений аргумента х и функции у(х) — сформировать самостоятельно.
При выборе команды Coefficients выводится окно сообщений с вычисленными коэффициентами а,b и с. При выборе команды Show в главном окне приложения отображаются график зависимости и исходные данные в виде точек. Окно должно содержать заголовок. Изображение должно занимать все окно.