Списки. Проблемы, возникающие при реализации списков массивами. Решение этих проблем в ООП. — КиберПедия 

Кормораздатчик мобильный электрифицированный: схема и процесс работы устройства...

Опора деревянной одностоечной и способы укрепление угловых опор: Опоры ВЛ - конструкции, предназначен­ные для поддерживания проводов на необходимой высоте над землей, водой...

Списки. Проблемы, возникающие при реализации списков массивами. Решение этих проблем в ООП.

2019-08-03 360
Списки. Проблемы, возникающие при реализации списков массивами. Решение этих проблем в ООП. 0.00 из 5.00 0 оценок
Заказать работу

Список — это абстрактный тип данных, представляющий собой упорядоченный набор значений, в котором некоторое значение может встречаться более одного раза. Значения, входящие в список называются элементами списка; если значение встречается несколько раз, каждое вхождение считается отдельным элементом. Список можно считать компьютерной реализацией математического понятия конечной последовательности.

C# имеет ряд классов для работы со списками. Они реализуют Интерфейс IList и наиболее популярной реализацией является общий список, часто называемый List<T>. T указывает Тип объектов, содержащихся в списке, который имеет дополнительное преимущество, что компилятор будет проверять и убедится, что вы добавите только объекты нужного типа в список - другими словами, List<T> является типобезопасным.

В классе ArrayList поддерживаются динамические массивы, расширяющиеся и сокращающиеся по мере необходимости. В языке C# стандартные массивы имеют фиксированную длину, которая не может изменяться во время выполнения программы. Это означает, что количество элементов в массиве нужно знать заранее. Но иногда требуемая конкретная длина массива остается неизвестной до самого момента выполнения программы. Именно для таких ситуаций и предназначен класс ArrayList. В классе ArrayList определяется массив переменной длины, который состоит из ссылок на объекты и может динамически увеличивать и уменьшать свой размер.

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

Если список двусвязный, то в каждом элементе храниться две ссылки: на соседа с большим номером и на соседа меньшим номером. Двусвязные списки так же бывают кольцевыми и не кольцевыми. Во втором случае в первом элементе есть ссылка на второй, а в последнем – на первый.

Пространства имен System Collections и System.Collections.Generic содержат классы, предназначенные для работы со списками.

Команда L=newArrayList(); создает пустой список с именем L.

Элементами этого списка являются объекты типа оbject. Поскольку все типы объявляются производными от класса оbject, то элементом списка может быть объект любого класса или структурного типа.

Метод Add позволяет добавлять элемент в конец списка

L.Add(3);

Данная команда добавит число 3 в список L.

L.Add(4);

Теперь список содержит два элемента <3,4>. Нумерация элементов списка начинается с 0.

Метод Insert (),позволяет вставить элемент в произвольное место списка. Его первый параметр – это номер позиции, в которую вставляется элемент, а второй параметр – сам элемент. Если указывается номер невозможной позиции, то формируется исключение.

 По команде L.Insert (1,5); число 5 будет помещено в позицию под номером один нашего списка. Список примет вид <3,5,4>.

Методы Remove At, Remove предназначены для удаления элементов из списка, первый удаляет элемент по номеру, второй – по значению. Для удаления нулевого элемента можно использовать команду

 L. Remove At(0);

 Если задать номер несуществующего элемента, то будет сгенерировано исключение.

Метод Remove удаляет элемент по значению, точнее, его первое вхождение. Если удаляемого элемента в списке нет, то ничего не происходит.

Доступ к элементу в классе ArrayList реализован по номеру, поэтому его можно считать динамическим массивом.

 int i= (int)L[0];

Переменная i получит значение 5. Преобразование типов при этом необходимо, поскольку элементы списка L имеют тип object.

 Команда L[0]=8; изменит значение элемента под номером 0, если задать номер несуществующего элемента, то будет сгенерировано исключение.

Свойство для чтения Count возвращает число элементов в списке.

Класс ArryList содержит много других членов, которые позволяют упростить работу со списками. Например, метод Sort сортирует список по возрастанию в тех случаях, когда это возможно, т.е. в тех случаях, когда типы элементов списка реализуют интерфейс IComparable.

Методы Contains и IndexOf позволяют выяснить содержится ли объект в списке, причём первый метод возвращает значение булева типа (true, false), а второй – номер первого вхождения элемента в список, если элемент есть, или «-1» в противном случае.

   Метод AddRange позволяет добавить к списку список элементов.

  L.AddRange(L);

   Данная команда удлинит список вдвое.

   Класс ArrayList в силу своей универсальности имеет недостатки. Например, в список чисел L, кто-то может добавить объект типа Button.

 

38. Обобщенные классы. Класс List<T>. Пример

В С-шарпе, начиная с версии.NET 2.0, можно при описании класса, структуры, интерфейса, метода или делегата объявлять используемые им типы как параметры. Типы, использующие параметры типов, называются обобщениями. Классы с параметрами типов называются обобщёнными классами (generic class). Терминология не устоялась и синонимами термина «обобщённый класс» являются термины: универсальный класс, родовой класс, параметризованный класс, класс с родовыми параметрами.

В языке С++ аналогами обобщенных классов являются шаблоны классов, но здесь просматривается только аналогия, между обобщениями и шаблонами есть не только сходство, но и различия. Следует особо подчеркнуть, что в C# всегда имелась возможность создавать обобщенный код, оперируя ссылками типа object. А поскольку класс object является базовым для всех остальных классов, то по ссылке типа object можно обращаться к объекту любого типа.

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

Описание обобщенного класса имеет следующий вид: class имя_класса {…} Например, class A {public T x;}

Обобщенный класс А имеет один параметр типа Т, в принципе параметров типов может быть сколько угодно, все они перечисляются через запятые. При создании ссылки, параметр типа конкретизируется: А a; A a1;A a2; При создании объекта класса, также указывается значение параметра типа: a= new A(); a1=new A(); A a4=new A (); Обобщенные классы чаще всего применяют для хранения данных, то есть в качестве контейнерных классов, или коллекций. Во вторую версию библиотеки.NET добавлены параметризованные коллекции для представления основных структур данных, применяющихся при создании программ — стека, очереди, списка, словаря и т. д.

Эти коллекции, расположенные в пространстве имен System.Collections.Generic, дублируют аналогичные коллекции пространства имен System.Collections. Обобщенный класс List позволяет построить строго типизированный список.

List L1=new List();

List< Button > L2=new List< Button >();

L1 – это список чисел,

L2 – список кнопок.

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

 

Обобщенные классы. Ограничение на базовый класс. Пример.

ООП-67

При работе с коллекциями мы оперируем с неизменяемыми элементами, выполняем только операции по перемещению и просмотру, не выполняем никаких операций над самими элементами, поэтому замена классов обобщениями дает только выигрыш. Если нам потребуется совершить какие-то действия над самими элементами, то встанет вопрос, а можно ли над элементами этого типа выполнить задуманные действия. Когда имеет место неограниченная универсальность, над объектами типов можно выполнять только те операции, которые допускают все типы, - в C# это эквивалентно операциям, разрешенным над объектами типа object, прародителя всех типов.

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

на этот параметр. Это делается с помощью оператора where при указании параметра типа:

class имя_класса<параметр_типа> where параметр_типа: ограничения { }

 

Ограничение на базовый класс Требует наличия определенного базового класса в аргументе типа. Это ограничение накладывается указанием имени требуемого базового класса.

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

Пример: после двоеточия пишется имя конкретного класса

class A <T,P> where T:M {…}

           

class M{ public int x;

public int f() {return x+4; }

 

class A <T> where T:M {

public T y;

public int g () {return y.f()+ y.x;}

Данное ограничение позволяет связать параметриз.типы обобщ.класса отношением наследования.

 

class B <T,P> where T:P {..}

Аргум.типа Т должен либо совпадать с аргументом типа Т, либо быть производным от аргумента типа Т.

 

Обобщенные классы. Ограничение на интерфейс. Пример.

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

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

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

Пример:

interface I1 {

inf f();}

class B:I1 {

public int f (){return 5;}

class A3 <T> where T:I1 {public T x;

public inf f () {return x.f();}

 

A3 <B> a6=new A3<B> ();

int i=a6.f(); //i=5

 

 

Ограничения new, class, struct. Пример.

Ограничение на конструктор требует предоставить конструктор без параметров в аргументе типа. Это ограничение накладывается с помощью оператора new().Ограничение new()на конструктор позволяет получать экземпляр объекта обобщенного типа.

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

Ограничение ссылочного типа: Требует указывать аргумент ссылочного типа с помощью оператора class.

class B <T,P> where T:class 

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

Среди всех этих ограничений чаще всего применяются ограничения на базовый класс и интерфейс, хотя все они важны в равной степени.

На один параметр типа может накладываться несколько ограничений. В этом случае ограничения указываются списком через запятую. В этом списке первым должно быть указано ограничение class либо struct, если оно присутствует, или же ограничение на базовый класс, если оно накладывается. Указывать ограничения class или struct одновременно с ограничением на базовый класс не разрешается. Далее по списку должно следовать ограничение на интерфейс, а последним по порядку — ограничение new(). Например.

 

class A <T> where T:Vector,new() {

public T z;

public T f(int k)

 

{

 

T v=new T();

 

v.x=k*z.x;

v.y=k*z.y;

return v;

}

 

}

class Vector{

public double x,y;

}

 

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

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

 class B<T, V> where V: T {…}

В этом объявлении оператор where уведомляет компилятор о том, что аргумент типа, привязанный к параметру типа V, должен быть таким же, как и аргумент типа, привязанный к параметру типа Т, или же наследовать от него. Если подобная связь отсутствует при создании объекта типа B, то во время компиляции возникнет ошибка.

 

Делегаты. Примеры.

Нередко возникает необходимость в том, чтобы созданные объекты посылали сообщения тем объектам, которые их породили. При программировании на С++ эта задача решается с помощью указателей на функции. Но в С – шарп указателей нет. Их исключили по мотивам безопасности. Указателей нет, но проблема есть, поэтому в С-шарп введена специальное средство, называемое делегатом.

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

Как все в мире С- шарп делегат –это специальный класс. Любой делегат производится от единого базового класса System. MulticastGelegate.

Итак делегат – это полезная синтаксическая конструкция, которая позволяет определять имя вызываемой функции во время выполнения программы, а не при компиляции.

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

Каждый делегат задает структуру тех методов, на которые они могут указывать.

Имя_делегата (список форм.параметров)

public delegate int D(int x, int y)

После того, как мы определили, на какой конкретно метод указывает этот делегат, мы можем вызвать метод, используя делегат.

public int f1 (int x, int y) {

    return x+y;}

public class A {

    public int f2 (int x, int z) { return x+y+2;}

public static int f3 (int x, int u) { return x+u+3;}

}

 

D d=f1; //в делегат попадает имя метода

int i=d(3,4); //i=7

A a = new A();

d=a.f2;                     i=d(3,4); //i=9

d=A.f3;                    i=d(3,4); //i=10

 

Функциональность делегата опред.на этапе выполнения программы.

Один делегат можно исп. На список методов. Добавл. метода в список +=

 

D d =f1;

i=d(3,4);

A a=new A();

d+=a.f2;

i=d(3,4);

d+=A.f3;

i=d(3,4);

//i=10

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

 

43.События. Использование делегатов.

Cобытие – тип, в котором определены события, как минимум поддерживает:

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

- отмену регистрации объектов, получающих уведомление о событии;

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

В языке С# есть тип event, который содержит в себе реализацию, всех этих действий.

Обработчик события – это обычный метод, который выполняет некоторые действия в программе, в случае если состоялось (сгенерировалось) событие.

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

Объявление события осуществляется на основе ранее объявленного типа делегата. Общая форма объявления события:

event делегат_события имя_события;

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

имя_события – конкретное имя объекта (переменной) типа «событие».

 

Пример. Объявление события с именем MyEvent на основе типа делегата MyDelType.

event MyDelType MyEvent;

Для того, чтобы можно было обрабатывать (запускать) списки обработчиков события, делегат не должен возвращать значение. То есть, делегат может возвращать тип void.

Чтобы организовать список обработчиков события (методов) нужно выполнить такую последовательность шагов:

1.Объявить тип делегата в классе.

2.Объявить событие в данном классе или создать другой класс, который содержит объявления события.

3.В некотором методе (программном коде) создать список обработчиков (методов), которые будут вызываться при вызове данного события. Это осуществляется с помощью операторов ‘=’ и ‘+=’. Создание списка означает регистрацию обработчиков для данного события.

4.Вызвать событие (запустить на выполнение) из этого метода.

Пример:

   // 1. Объявить тип делегата

   delegate void CalcFigure(double R);

   // 2. Объявить событие с именем ECF

   event CalcFigure ECF;

   // 3. Методы обработки события - размещаются в этом же классе

   // Методы имеют точно такую же сигнатуру как тип делегата CalcFigure

   // Длина окружности на основе радиуса R

   void GetLength(double R)

   {

       double res;

       const double Pi = 3.1415;

       res = 2 * Pi * R;

       label1.Text = "Длина окружности = " + res.ToString(); // вывести на форму

   }

   // Площадь круга

   void GetArea(double R)

   {

       double res;

       const double Pi = 3.1415;

       res = Pi * R * R;

       label2.Text = "Площадь круга = " + res.ToString(); // результат - на форму

   }

   // Объем шара

   void GetVolume(double R)

   {

       double res;

       const double Pi = 3.1415;

       res = 4.0 / 3.0 * Pi * R * R * R;

       label3.Text = " Объем шара = " + res.ToString();

   }

public Form1()

{ InitializeComponent();

}

private void button1_Click(object sender, EventArgs e)

{

// Демонстрация работы с методами с помощью события

       //Создать цепочку методов, которые будут вызываться из события ECF

    ECF = GetLength; // ECF => GetLength()

       ECF += GetArea; // ECF => GetLength() -> GetArea()

       ECF += GetVolume; // ECF => GetLength() -> GetArea() -> GetVolume()

// Вызов события ECF с параметром из текстбокса

       ECF(ConvertToDouble (textBoxV. Text)); // вызываются последовательно три метода GetLength(), GetArea(), GetVolume()

   }

}

}

 

События. Классы-издателя и классы-подписчики.Примеры.

ООП84

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

Когда событие происходит, вызываются все зарегистрированные обработчики события. Класс, генерирующий события, называется издателем, а классы, обрабатывающие поступившую информацию, называются подписчиками. Издатель определяет, когда возникает событие, какая информация сопутствует событию, подписчики определяют, какие действия выполняются в ответ на событие. У события может быть любое количество разнотипных подписчиков. Подписчик может обрабатывать несколько событий от нескольких издателей. С любым событием может быть связана информация. Существуют рекомендации, которые гласят, что у обр.события должно быть 2 парам:первый-ссылка на объект, генер.событие, второй-параметр типа производного от библиот.класса EventArgs, содержащий допинформацию, которая потребуется разработчику. То есть,.NET-совместимые обработчики имеют вид:

void Обработчик (Object издатель, НаследникКласса EventArgs e)

Как правило, издатель – это параметр, передаваемый вызывающим объектом с помощью this, а параметр е содержит инф.о объекте.

Пример: почта, организация рассылки писем.

public class Письмо:EventArgs

                   {

                   public string кому, отКого, содержание;

                   public Письмо(string кому, string отКого, string содержание)

                              {

                                          this. кому = кому;

                                          this. отКого = отКого;

                                          this. содержание = содержание;

                              }

                   }

тот прототип, что и методы реализующие реакцию на событие.

public delegate void Получить (Object sender, Письмо e);

public class Почта

       {

                   public event Получить ПришлоПисьмо;         

public void РазослатьПисьма (string кому, string отКого, string содержание)

                   {

                              Письмо e=new Письмо(кому, отКого, содержание);

                              if(ПришлоПисьмо!=null)ПришлоПисьмо(this,e);

                   }

       }

В классе Почта создается событие (event) под названием ПроишлоПисьмо.

Условие ПришлоПисьмо!=null проверяет есть ли объекты, желающие получить известие об этом событии, а команда ПришлоПисьмо(this,e); рассылает сообщения, всем заинтересованным объектам.

Создадим класс Адресат, объекты которого могут реагировать на события.

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

public class Адресат

    {

              public int i;

              public Адресат ()

              {           

              }

              private void ПолучитьПисьмо (Object sender, Письмо e)//

              {

                       i++;

              }

              //

              public void Отключиться (Почта e)

              {

                       Получить g=new Получить (ПолучитьПисьмо);

                       e.ПришлоПисьмо-=g;

              }

              public void Подключиться (Почта e)

              {

                       Получить g=new Получить(ПолучитьПисьмо);

                       e.ПришлоПисьмо+=g;

              }

    }

Лямбда-выражения. Примеры.

Для улучшения читабельности программы небольшие методы оформить как лямбда-выражения. С ними можно работать ч/з делегаты. Лямбда-выражения относятся к анонимным методам

Простое лямбда-выражение:

(список перем.без указания типа) => выражение.

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

a = > a.x;

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

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

Количество параметров выражения равно количеству параметров делегата. Лямбда-выражения — это код, который может быть представлен как делегат или дерево выражений, которое компилируется в делегат

Пример:

Delegate void Hello (); //делегат без параметров

Hello hello1= () = > Console.Writeline (“Hello”);

Hello hello2= () = > Console.Writeline (“Welcome”);

hello1(); //Hello

hello2(); //Welcome

 

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

Вызвать метод для подсчета объектов, у которых: 1) первое поле >3 2) второе поле >5.

public class Rectangle1

{ public int dlina, shirina;

   public double area () {return dlina*shirina;}

public Rectangle1 (int xx, int yy) { dlina=xx; shirina=yy;}

} }

 

Rectangle1 [] mas={ new Rectangle1(11,4),new Rectangle1(9,4),new Rectangle1(1,2), new Rectangle1(3,5), new Rectangle1 (8,7), new Rectangle1 (9,7), new Rectangle1 (15,4)};

 

              public delegate bool Del1 (Rectangle1 aobject);

              public int Fun (Del1 d, Rectangle1 [] mas)

              {   int k=0;

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

                                 if (d(mas[i])==true)

                                          k++;

                       return k;

              }

void Button1Click(object sender, EventArgs e) {

int i1 =Fun ((aobject)=> {if (aobject.dlina>3) return true; else return false;}, mas); //лямбда-выражение

label1.Text=Convert.ToString(i1); }

void Button2Click(object sender, EventArgs e)

{ int i2 =Fun ((aobject)=> {if (aobject.shirina>5) return true; else return false;}, mas);

label2.Text=Convert.ToString(i2);

}

    }

 

46

 


Поделиться с друзьями:

Организация стока поверхностных вод: Наибольшее количество влаги на земном шаре испаряется с поверхности морей и океанов (88‰)...

Наброски и зарисовки растений, плодов, цветов: Освоить конструктивное построение структуры дерева через зарисовки отдельных деревьев, группы деревьев...

История развития хранилищ для нефти: Первые склады нефти появились в XVII веке. Они представляли собой землянные ямы-амбара глубиной 4…5 м...

История создания датчика движения: Первый прибор для обнаружения движения был изобретен немецким физиком Генрихом Герцем...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.009 с.