Синтаксис объявления интерфейсов

Объявление интерфейса осуществляется с помощью служебного слова interface и имеет следующий вид:

[модификатор доступа] interface имя_интерфейса

{

// члены интерфейса

}

Название интерфейса принято начинать с заглавной буквы I. Например, основной работой исследователя является исследования и изобретения, тогда интерфейс исследователя можно описать следующим образом:

public interface I_Researcher

{

void Investigate();

void Invent();

}

В C# можно создавать интерфейсную переменную ссылки. Она может указывать на любой экземпляр любого класса, который наследует данный интерфейс. При вызове мето да объекта с помощью ссылки выполняется та версия метода, которая реализовывается в этом объекте. Через интерфейсные ссылки можно вызывать только методы данного интерфейса. Если понадобится вызвать метод, который не является частью данного интерфейса, то необходимо привести ссылку к соответствующему типу.

Пример:

public interface I_Task {

String Description {get;}

void perform();

}

 

public class SynopticsTask : I_Task {

private readonly String description = "Load synoptics data for some region";

private readonly String city;

public SynopticsTask(String city) {

this.city = city;

}

public void perform() {

// TODO: connect to internet, parse response, print results.

}

 

public String Description {

get {return description;}

}

}

 

public class CleanupTask : I_Task {

public void perform() {

//TODO: Recursive search for folders 'Temp', 'tmp', clean up them.

}

 

public String Description {

get { return "Task for removing all temporary files on disk"; }

}

}

 

class Program

{

public static void Main(string[] args) {

var tasks = new I_Task[] {new SynopticsTask("ZP"), new CleanupTask()};

foreach (I_Task task in tasks) {

Console.WriteLine(task.Description);

task.perform();

}

}

}

Результат:

Load synoptics data for some region

Task for removing all temporary files on disk

Иногда нужно объявить класс, который наследует несколько интерфейсов. Может возникнуть ситуация, когда у этих интерфейсов есть методы с одинаковым названием.

Например, есть два интерфейса:

public interface I_Human

{

object Work();

}

public interface I_Inferior

{

object Work();

}

Мы хотим создать класс, который наследует оба эти интерфейса. Он должен реализовать все методы, которые есть в каждом интерфейсе.

public class Employee: I_Human, I_Inferior

{

//реализация метода объявленного в I_Human

object I_Human.Work() {...}

//реализация метода объявленного в I_Inferior

object I_Inferior.Work() {...}

}

Теперь возникает вопрос: когда мы будем вызывать метод Work() для экземпляра класса Employee, какой из двух методов будет вызван?

Для того чтобы вызвать нужный метод, необходимо привести тип к нужному интерфейсу. Рассмотрим пример:

//Создадим экземпляр класса Employee:

Employee john = new Employee();

I_Human human = john;

human.Work(); //вызываем метод I_Human.Work

I_Inferior inferior = john;

inferior.Work(); //вызываем метод I_Inferior.Work

Ключевые слова

Определить, поддерживает ли данный тип тот или иной интерфейс, можно с использованием ключевого слова as. Если объект удается интерпретировать как указанный интерфейс, то возвращается ссылка на интересующий интерфейс, а если нет, то ссылка null. Следовательно, перед продолжением в коде необходимо предусмотреть проверку на null.

Пример:

IInfo obj = ui1 as IInfo;

if (obj != null)

Console.WriteLine("Тип UI поддерживает интерфейс IInfo");

else

Console.WriteLine(":(");

Проверить, был ли реализован нужный интерфейс, можно также с помощью ключевого слова is. Если запрашиваемый объект не совместим с указанным интерфейсом, возвращается значение false, а если совместим, то можно спокойно вызывать члены этого интерфейса без применения логики try/catch.

Пример:

if (ui1 is IInfo)

Console.WriteLine("Тип UI поддерживает интерфейс IInfo");

else

Console.WriteLine(":(");

Стандартные интерфейсы

IEnumerable(поддерживает перебор всех элементов)

Свойства и методы Описание
GetEnumerator Возвращает перечислитель, который осуществляет перебор элементов коллекции.

ICollection(Наследует IEnumerable, имеет дополнительные свойства и методы)

Свойства и методы Описание
Count Свойство, которое возвращает целое число –количество элементов коллекции.
IsSynchronized Свойство, которое возвращает значение true, если доступ к классу ICollection является синхронизированным (потокобезопасным), в противном случае — false. Для массива это свойство всегда возвращает – false
CopyTo(Array array, int index) Копирует элементы коллекции ICollection в массив array, начиная с указанного индекса массива Array index.

IList(Наследует ICollection, является базовым интерфейсом для всех неуниверсальных списков)

Свойства и методы Описание
int Add(object value) Добавляет в коллекцию элемент value. Возвращает номер позиции, в которую был добавлен элемент.
void Clear() Данная процедура удаляет все элементы из списка.
bool Contains(object value) Возвращает true, если элемент value содержится в списке. Иначе возвращает false.
int IndexOf(object value) Возвращает индекс элемента value в данном списке.
void Insert(int index, object value) Вставляет элемент value в список на заданную позицию.
void Remove(object value) Удаляет первое вхождение элемента value в списке.
void RemoveAt(int index) Удаляет из списка элемент, который располагается по указанному индексу (index).
bool IsFixedSize Свойство, которое возвращает true, если список имеет фиксированный размер. Иначе -false.
bool IsReadOnly Возвращает true, если коллекция доступна только для чтения. Иначе – false.
object Item[int index]{get;set;} Получает или задает объект по заданному индексу.

IDisposable(Этот интерфейс является альтернативой деструктору. Определенные в нем методы позволяют освобождать неуправляемые ресурсы)

Свойства и методы Описание
void Dispose() Выполняет удаление, высвобождение и сбор неуправляемых ресурсов.

IComparable(Используется для сортировки элементов. Содержит метод, который определяет, как будут сравниваться элементы)

Свойства и методы Описание
int CompareTo(object obj) Сравнивает текущий экземпляр с другим объектом того же типа и возвращает целое число, которое показывает, расположен ли текущий экземпляр перед, после или на той же позиции в порядке сортировки, что и другой объект.

Пример использования интерфейсов:

public class Student : IComparable<Student> {

private String name;

private String group;

private float avgScore;

 

public Student(String name, String group, float avgScore) {

this.name = name;

this.group = group;

this.avgScore = avgScore;

}

 

public String Name {

get {return name;}

}

 

public String Group {

get {return group;}

}

 

public float AvgScore {

get {return avgScore;}

}

 

public int CompareTo(Student compareStudent) {

// A null value means that this object is greater.

if (compareStudent == null)

return 1;

else

return this.name.CompareTo(compareStudent.name);

}

}

Student[] studentArray = new Student[5];

studentArray[0] = new Student("Ivanov", "510", 4.7f);

studentArray[1] = new Student("Petrov", "510", 3.7f);

studentArray[2] = new Student("Sobolev", "530", 4.2f);

studentArray[3] = new Student("Smirnov", "530", 5.0f);

studentArray[4] = new Student("Sidorov", "520", 4.0f);

 

List<Student> studentList = new List<Student>();

//foreach over IEnumerable interface

foreach(Student student in studentArray) {

Console.WriteLine(student.Name);

studentList.Add(student);

}//Ivanov Petrov Sobolev Smirnov Sidorov

studentList.Sort(); //Sort alphabetically using Student.CompareTo()

foreach(Student student in studentList) {

Console.WriteLine(student.Name);

}

//Ivanov Petrov Sidorov Smirnov Sobolev

ЛЕКЦИЯ 10

Абстрактные классы

C# позволяет использовать абстрактные классы и методы. Абстрактный класс – это класс, экземпляры которого создавать нельзя, но его могут наследовать другие классы.

Для объявления такого класса используют ключевое слово abstract.

Абстрактный класс будет полезен, если несколько классов имеют общие свойства и методы.

При необходимости классы-наследники могут переопределить или закрыть наследуемые элементы базового абстрактного класса.

Пример:

//Реализация методов, общих для всех телефонов

public abstract class MobilePhone {

 

protected void sendMessage(String message, String phoneNumber) {

//check GSM connection

//check balance

//send sms

//handle response success/failed

Console.WriteLine("You have send '" + message + "' from " + phoneNumber);

}

 

protected void performCall(String phoneNumber, String myPhone) {

//init Call

//handle call lifecycle

Console.WriteLine("You have performe call to " + phoneNumber + " from " + myPhone);

}

 

protected String getDefaultPhoneNumber() {

return "066-550-94-88";

}

}

 

//Кнопочный телефон

public class ButtonMobilePhone : MobilePhone {

 

private String input;

 

//Вызывается при нажатии кнопок для совершения звонка. Кнопки 0-9 сохраняются в поле input в качестве номера телефона.

//Затем при нажатии кнопки call осуществляется вызов.

public void handleButton(String buttonType) {

switch (buttonType) {

case "0":

case "1":

case "2":

case "3":

case "4":

case "5":

case "6":

case "7":

case "8":

case "9": //сохранение цифры номера

input += buttonType;

break;

case "cancel": //сброс ввода, очистка input

input = "";

case "call": //вызов базового метода совершения звонка

performCall(input, getDefaultPhoneNumber());

break;

}

}

}

 

//Andoird телефон, наследует MobilePhone

public class AndroidMobilePhone : MobilePhone {

 

//Вызывается при запросе дествия пользователем

public void handleIntent(String type, String extra) {

switch(type) {

case "sendMessage": //отпрвка смс (реализовано в базовом классе)

sendMessage(extra, getDefaultPhoneNumber());

break;

case "initCall": //совершение звонка (реализовано в базовом классе)

performCall(extra, getDefaultPhoneNumber());

break;

}

}

}

 

//Android телефон с двумя симкартами

public class DualSimAndroidPhone : AndroidMobilePhone {

private readonly String PHONE_1 = "066-550-94-88";

private readonly String PHONE_2 = "099-005-16-22";

//Номер активой симкарты

private int currentPhone = 1;

 

//Вызывается при запросе действия пользователем

public void handleIntent(String type, String extra) {

switch(type) {

case "switchPhone": //переключение активной симкарты

if (currentPhone == 1) {

currentPhone = 2;

} else {

currentPhone = 1;

}

break;

default://Тип действия не переключения симкарты - вызов базового метода

//AndroidMobilePhone.handleIntent(); - проверка типа действия на соответствие 'sendMessage' или 'initCall'

base.handleIntent(type, extra);

}

}

 

//Переопределение MobilePhone.getDefaultPhoneNumber()

protected String getDefaultPhoneNumber() {

if (currentPhone == 1) {

return PHONE_1;

}

return PHONE_2;

}

}

Виртуальные методы

Иногда необходимо изменить методы, которые наследуются от базового класса. Для того чтобы была возможность его изменить используют виртуальные методы. Объявив метод или свойство класса как виртуальные, вы тем самым позволяете классам наследникам переопределять данный метод или свойство. Для этого используется ключевое слово virtual Например в классе Human виртуальный метод Work(), каждый из классов-наследников Manager, Tutor, Student и т.д. может переопределить соответственно своих функций.

• Поля-члены и статические методы не могут быть объявлены как виртуальные.

• Применение виртуальных методов позволяет реализовывать механизм позднего связывания.

• На этапе компиляции строится только таблица виртуальных методов, а конкретный адрес метода, который будет вызван, определяется на этапе выполнения.

При вызове метода - члена класса действуют следующие правила:

• для виртуального метода вызывается метод, соответствующий типу объекта, накоторый имеется ссылка;

• для не виртуального метода вызывается метод, соответствующий типу самойссылки.

При позднем связывании определение вызываемого метода происходит на этапе выполнения (а не при компиляции) в зависимости от типа объекта, для которого вызывается виртуальный метод.

Для того чтобы переопределить метод в классе-наследнике, используют ключевое слово override.

Пример:

public abstract class AliveCreature {

 

public virtual void action() {

//perform tipical actions for creature

}

 

 

protected virtual void die() {

//clean up resources & object

}

}

 

public class Plant : AliveCreature {

public override void action() {

//ростить корни/побеги

//производить кислород

}

}

 

public class Bird : AliveCreature {

public override void action() {

//летать

}

}

 

public class Human : AliveCreature {

public override void action() {

//работать

}

}

 

public class Student : Human {

public override void action() {

//учиться

}

}

Ключевое слово sealed

Иногда возникают ситуации, когда необходимо запретить наследовать некоторый класс или переопределять некоторый метод. Для этого класс или метод нужно объявить как терминальный. Для этого используют ключевое слово sealed. Объявим терминальный класс:

public sealed class Tutor : Human { }

Никакие другие классы не могут наследовать класс Tutor. При попытке это сделать компилятор выдаст ошибку. При этом сам класс Tutor может быть наследован от другого класса.

Аналогично можно запретить переопределять свойства и методы базового класса. Если член базового класса имеет модификатор наследования virtual, тогда наследник может закрыть дальнейшее переопределение члена. Разрешим переопределение наследуемыми классами метода Work() класса Human.

class Human

{

public virtual void Work()

{

// Do something

}

}

Тогда класс Employee может переопределить метод Work() и закрыть возможность его переопределения для собственных наследников.

public class Employee : Human

{

public sealed override void Work()

{

// Do something great

}

}

ЛЕКЦИЯ 11

Структуры

В C#.NET структуры предназначены для группирования и хранения небольших порций данных.

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

Для объявления структуры используют ключевое слово struct:

struct Имя : Интерфейсы

{

// объявления членов

}

В структурах, как и в классах, возможно определение полей, свойств, методов, конструкторов, итераторов и событий.

Пример:

struct Car {

private string model;

private float fuel;

 

public Car(string model) {

this.model = model;

this.fuel = 0f;

}

 

public Car(string model, float fuel) {

this.model = model;

this.fuel = fuel;

}

 

public float Fuel {

get {

return fuel;

}

}

 

public void run(long distance) {

//calculate fuel based on passed distance

fuel -= distance / 100 * 7;

}

}

Особенности структур

Структуры используют для оптимизации производительности, когда из функциональности класса нужно только хранение некоторых небольших порций данных.

Тип struct не требует отдельной ссылочной переменной. Это означает, что при использовании структур расходуется меньший объем памяти, и благодаря прямому доступу к структурам, при работе с ними не снижается производительность, что имеет место при доступе к объектам классов.

При объявлении все поля структуры автоматически инициализируются нулевыми значениями.

За счет хранения в стеке выделение и удаление памяти под структуры происходит очень быстро.

При присваивании одной структуры другой или при передаче методу структуры, как параметра, происходит полное копирование содержимого структуры, что намного медленнее передачи ссылки. (Скорость копирования зависит от размера структуры: чем больше полей – тем дольше копирование).

Структуры – это типы значений и наследуются от System.ValueType.

Структуры можно трактовать как упрощенные классы.

Структуры имеют доступ к методам класса System.Object, которые могут переопределять. Структуры не поддерживают наследование. Структуры могут наследовать интерфейсы. Компилятор всегда генерирует конструктор по умолчанию без параметров, который переопределить невозможно.

Выделение памяти под всю структуру происходит при объявлении переменной. Соответственно, операция new для структур действует иначе, чем для ссылочных типов: она вызовет инициализацию полей значениями по умолчанию.

Конструктор по умолчанию инициализирует все поля нулевыми значениями. Также невозможно обойти конструктор по умолчанию, определяя начальные значения полей.

Перечисления

Перечисление (enumeration) – это непустой список именованных констант. Он задает все значения, которые может принимать переменная данного типа. Перечисления являются классом и наследуются от базового класса System.Enum.

Для объявления перечисления используется ключевое слово enum, указывается имя перечисления и в фигурных скобках перечисляются имена констант:

enum EnumName {elem1, elem2, elem3, elem4}

Обращение к элементу перечисления осуществляется указыванием имени класса перечисления и через точку имени конкретного элемента перечисления.

Константы перечисления имеют целочисленный тип. По умолчанию, первой константе присваивается значение 0, а значение каждой следующей константы увеличивается на единицу. Также можно присвоить значение константе явно.

С переменными типов перечислений можно осуществлять арифметические операции.

Пример:

enum Action {

INITIAL, LOAD_INPUTS, PARSE_INPUTS, PERFORM_CALCULATIONS, SAVE_OUTPUTS, FINISHED

}

 

class Task {

private Action state = Action.INITIAL;

public void perform() {

while (state != Action.FINISHED) {

performCurrentState();

Console.WriteLine("Current state is " + state);

}

}

private void performCurrentState() {

switch(state) {

case Action.INITIAL:

//init task state

state = Action.LOAD_INPUTS;

break;

case Action.LOAD_INPUTS:

//loading input information

state = Action.PARSE_INPUTS;

break;

case Action.PARSE_INPUTS:

//transforming inputs

state = Action.PERFORM_CALCULATIONS;

break;

case Action.PERFORM_CALCULATIONS:

//core calculations

state = Action.SAVE_OUTPUTS;

break;

case Action.SAVE_OUTPUTS:

//writing results

state = Action.FINISHED;

break;

case Action.FINISHED:

//clear temp information

break;

}

}

}

Результат:

Current state is LOAD_INPUTS

Current state is PARSE_INPUTS

Current state is PERFORM_CALCULATIONS

Current state is SAVE_OUTPUTS

Current state is FINISHED

Особенности перечислений

Перечисления позволяют сделать процесс программирования более удобным и быстрым, помогают избавиться от путаницы при присвоении переменным значений. Можно выделить следующие полезные функции перечислений:

- перечисления гарантируют, что переменным будут присваиваться только разрешенные, ожидаемые значения. Если вы попробуете присвоить экземпляру перечисления значение, которое не входит в список допустимых значений, то компилятор выдаст ошибку;

- перечисления делают код более понятным, так как мы обращаемся не просто к числам, а к осмысленным именам;

- как уже упоминалось, перечисления наследуются от базового класса System.Enum, что позволяет вызывать для них ряд полезных методов(CompareTo(), Equals(), GetName(), getValues(), Parse()...).

Делегаты

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

Для объявления делегата используют ключевое слово delegate. После него указывается возвращаемый тип, имя делегата и в скобках сигнатура делегируемых методов:

delegate Type DelegateName(list)

Type – возвращаемый тип, DelegateName – имя делегата, list – список параметров,

необходимых методам при вызове их с помощью делегата (этот список может быть пустым, то есть делегат может вызывать метод, который не имеет аргументов).

Объявленный делегат может вызвать только те методы, которые возвращают указанный тип Type и принимает список параметров list. Никаких других методов делегат вызвать не может. Делегат может вызывать как методы экземпляров класса, так и статические методы. Также к делегатам можно применять один из модификаторов доступа:

public, protected, private, internal, protected internal.

Пример:

private struct Person {

public String FirstName;

public String LastName;

public DateTime BirthDay;

public Person(String firstName, String lastName, DateTime birthDay) {

this.FirstName = firstName;

this.LastName = lastName;

this.BirthDay = birthDay;

}

public override String ToString() {

return String.Format("Имя: {0}; Фамилия: {1}; Дата рождения: {2:d}.", FirstName, LastName, BirthDay);

}

public static String GetTypeName() { return "Человек"; }

}

 

private delegate string GetAsString();

 

static void Main(string[] args) {

DateTime birthDay = new DateTime(1978, 2, 15);

Person person = new Person("Иван", "Петров", birthDay);

GetAsString getStringMethod =

new GetAsString(birthDay.ToLongDateString);

Console.WriteLine(getStringMethod());

getStringMethod = person.ToString;

Console.WriteLine(getStringMethod());

getStringMethod = Person.GetTypeName;

Console.WriteLine(getStringMethod());

}

 

Результат:

15 февраля 1978 г.

Имя: Иван; Фамилия: Петров; Дата рождения: 15.02.1978.

Человек

ЛЕКЦИЯ 12

События

Использование событий – это один из наиболее встречающихся в .NET приемов ООП.

Можно сказать, что событие – это извещение о возникновении некоторого действия. Это может быть нажатие кнопки, выбор элемента списка и т.д.

Отправителем события может быть любой компонент формы, либо само приложение.

Отправитель – это объект, который генерирует событие. Так объект-отправитель оповещает остальные объекты о том, что произошло определенное действие. Получить уведомление о событии может любой другой объект, которому это необходимо. Будем называть такие объекты получателем события. Для того чтобы объект знал о возникновении события, его необходимо подписать на это событие. Это означает, что нужно задать метод - обработчик этого события. Причем сигнатура этой функции должна полностью совпадать с сигнатурой обрабатываемого события. Важно отметить, что отправитель сообщения не знает о существовании получателя, не имеет на него ссылок.

Для реализации событий используются делегаты. Объект-отправитель определяет делегат, а каждый объект-получатель добавляет свой метод-обработчик в цепочку ссылок делегата. Вызов цепочки методов осуществляет объект-отправитель, инициируя выполнение всех методов. Порядок вызова методов подписанных объектов однозначно не определен.

Для объявления события используют ключевое слово event:

event DelegateName EventName;

DelegateName – имя используемого делегата; EventName – имя события.

Напомним, что объект-отправитель определяет делегат, который должен быть реализован объектами-подписчиками. При возникновении события, методы подписчиков (обработчики события) вызываются через этот делегат. Если на событие нет подписанных объектов, то оно имеет значение null, а его вызов вызовет ошибку выполнения.

Существует соглашение, по которому делегаты обработчиков событий возвращают тип void и имеют два параметра. Первый – объект-отправитель, то есть объект генерирующий событие (тип object), а второй – объект содержащий информацию и параметры события, производный от класса EventArgs.

Пример:

public class Person {

public event EventHandler WorkEnded;

public String FirstName;

public String LastName;

public DateTime Birthday;

public Person(String firstName, String lastName, DateTime birthday) {

this.FirstName = firstName;

this.LastName = lastName;

this.Birthday = birthday;

}

public void Work() {

// Do something

if (WorkEnded != null) WorkEnded(this, EventArgs.Empty);

}

}

static void Person_WorkEnded(object sender, EventArgs e) {

Console.WriteLine("Работа выполнена ! \n");

}

static void Main(string[] args) {

Person person = new Person("Bill", "Gordon", new DateTime(1962, 8, 11));

person.WorkEnded += new EventHandler(Person_WorkEnded);

person.Work();

}

Для создания события был использован стандартный делегат EventHandler использующийся для генерации событий без параметров.

Главное достоинство событий в том, что они позволяют сделать объект-отправитель и объекты-получатели независимыми друг от друга, так как они отделены делегатом. Это делает код более гибким, позволяет разработчику менять реакцию на событие, добавлять новых подписчиков.

Коллекции

Необходимость возможности группирования данных неоспорима. Как правило, это первое, что обсуждается при рассмотрении темы массивы, в чём мы тоже не оригинальны. Любой набор данных, объектов можно назвать коллекцией. Массив из пространства имён System.Array – это тоже своего рода коллекция, но статичная, его нельзя ни расширить, ни сжать при необходимости, а это не всегда удобно.

.NET представляет множество классов коллекций, позволяющих работать с наборами данных более гибко, чем массив. Что собой представляет коллекция? Это группа элементов. В .NET коллекции содержат объекты, в том числе и упакованные типы значений. Каждый объект, содержащийся в коллекции, называется её элементом. Некоторые коллекции хранят данные как прямой список элементов, другие содержат списки пар ключей и значений. К первым относятся классы System.Collections.ArrayList, System.Collections.Queue, System.Collections.Stack, System.Collections.BitArray, ко вторым (называемым также словарями) относятся такие классы, как, например, System.Collections.SortedList, System.Collections.Hashtable.

Все классы коллекций находятся в пространстве имён System.Collections.

System.Collections.ArrayList class – этот класс как никакой другой подобен массиву, но в отличие от массива, может расширяться. Создать экземпляр этой коллекции очень просто:

ArrayList arr = new ArrayList();

В данном случае мы создаём объект коллекции, не задавая явно его вместимость. Вместимость ArrayList определяется количеством элементов, которые он может содержать. Просмотреть вместимость данной коллекции можно с помощью свойства Capacity:

Console.WriteLine("Capacity: {0}", arr.Capacity);

Можно задать вместимость коллекции при её создании, явно указав количество элементов в конструкторе:

ArrayList arr1 = new ArrayList(5);

Console.WriteLine("Capacity: {0}", arr1.Capacity);

// Capacity: 5

При добавлении первых элементов в ArrayList его вместимость автоматически устанавливается. Изначально она равна 4 элементам. ArrayList может принимать null как значение и допускает добавление повторяющихся элементов. А свойство Count показывает фактичекое количество элементов коллекции.

System.Collections.Stack class – следующий класс коллекций, который мы рассмотрим. Это тип коллекции, идеально подходящий для временного размещения объектов, которые после использования будут удалены. Эта коллекция работает по принципу LIFO (last-infirst-out).

То есть последовательность извлечения объектов противоположна последовательности их помещения в коллекцию. Создать эту коллекцию так же просто, как предыдущую:

Stack s = new Stack();

s.Push("one");

s.Push("two");

s.Push("three");

s.Pop();

Если нам нужно получить значение элемента, который был помещён в коллекцию последним, необходимо применить метод Peek(), который не удаляет элемент из коллекции, в отличие от Pop():

System.Collections.Queue class – главным отличием данной коллекции от Stack заключается в принципе FIFO (first-in-first-out), по которому работает очередь. Это значит, что элементы извлекаются из коллекции в той же последовательности, в которой они были в неё помещены. Индексация к элементам данной коллекции не применяется. Создание очереди:

Queue q = new Queue();

Основными методами очереди являются методы Enqueue() и Dequeue() для помещения элементов в коллекцию и извлечения соответственно.

Queue q = new Queue();

for (int i = 1; i < 3; i++) {

q.Enqueue(i);

}

System.Collections.SortedList class – представляет собой коллекцию пар объектов ключ-значение, которые сортируются по ключу и доступ к элементам которой может быть получен по ключу и по индексу.

SortedList list = new SortedList();

list.Add(2, 20);

list.Add(1, 100);

list.Add(3, 3);

foreach (int i in list.GetKeyList())

Console.WriteLine(i);

foreach (int i in list.GetValueList())

Console.WriteLine(i);

//1

//2

//3

//100

//20

//3

Обратим внимание, что элементы, помещённые в SortedList автоматически будут отсортированы по ключу. Для удаления элемента из списка по заданному значению ключа применяется метод Remove() , а для удаления по заданному индексу – метод RemoveAt().

System.Collections.Hashtable class – представляют собой наборы данных, содержащих пары ключ-значение, причём оба они относятся к типу Object, а это значит, что тип и ключа, и его парного значения может быть совершенно любой. Эта коллекция относится к так называемой группе словарей (по аналогии с реальным словарём), где по ключу мы можем получить доступ к значению.

Создать коллекцию заполнить её данные можно так:

Hashtable hash = new Hashtable();

hash.Add(1, "one");

hash.Add(2, "two");

hash.Add("three", 3);

Существуют и дргуие коллекции, они являются узкоспециализированными, например:

1. System.Collections.Specialized.ListDictionary class – класс ведёт себя очень похоже на Hashtable, но превосходит его по скорости при работе с 10 и менее элементами. При работе с большим количеством элементов его использовать не рекомендуется.

2. System.Collections.Specialized.HybridDictionary class – состоит из двух встроенных коллекций , ListDictionary и Hashtable. Одновременно используется только один из этих классов. ListDictionary используется, пока коллекция содержит 10 или менее элементов, а затем происходит переключение на использование Hashtable. Если коллекция уже переключилась на Hashtable, переключиться обратно на ListDictionary уже нельзя, даже если количество содержащихся элементов станет 10 и менее. При использовании элементов типа string в качестве ключа класс позволяет применять как чувствительный к регистру поиск, так и нечувствительный. Это определяется установкой булевой переменной в конструкторе.

3. System.Collections.Specialized.CollectionsUtil class – создаёт коллекции, игнорирующие регистр в строках. Этот класс содержит 2 статических метода: один для создания регистронезависимого Hashtable, а другой - для создания регистронезависимого SortedList.

4. System.Collections.Specialized.NameValueCollection class – эта коллекция состоит из пар ключ – значение, оба из которых относятся к типу string. Особенностью этой коллекции является то, что она может хранить множество значений при единственном ключе.

5. System.Collections.Specialized.StringCollection class – это обычный список элементов типа string. В неё можно помещать null элементы и дублированные элементы. Этот список чувствителен к регистру. Доступ к элементам данной коллекции можно получить по целочисленному индексу.

6. System.Collections.Specialized.StringDictionary class – это Hashtable, содержащий ключи и значения типа string. Прежде чем быть добавленными в коллекцию, ключи полностью преобразуются к нижнему регистру, что позволяет осуществлять сравнение, не чувствительное к регистру. Ключ не может быть null, в отличие от значения.

Лекция 13

MDI приложения