Программирование

Автор работы: Пользователь скрыл имя, 28 Марта 2012 в 11:49, курс лекций

Описание

Лекции по дисциплине "Программирование"

Содержание

Лекция 1. Состав языка Типы данных Переменные и операции

Лекция 2. Линейные программы

Лекция 3. Простейшие операторы. Операторы ветвления

Лекция 4. Операторы цикла и передачи управления

Лекция 5. Обработка исключительных ситуаций

Лекция 6. Классы: основные понятия Описание класса

Лекция 7. Параметры методов

Лекция 8. Конструкторы и свойства

Лекция 9. . Массивы

Лекция 10. Символы и строки

Лекция 11 Дополнительные возможности методов. Индексаторы

Лекция 12. Операции класса. Деструкторы

Лекция 13. Наследование классов

Лекция 14. Интерфейсы

Лекция 15. Стандартные интерфейсы .NET

Лекция 16. Структуры и перечисления

Лекция 17. Делегаты

Лекция 18. События

Работа состоит из  1 файл

Лекции C#.doc

— 847.00 Кб (Скачать документ)

using System;

namespace ConsoleApplication1

{

    public delegate void Del( object o );             // объявление делегата

 

    class Subj                                             // класс-источник

    {

        Del dels;                          // объявление экземпляра делегата

         public void Register( Del d )                // регистрация делегата

        {

            dels += d;

        }

         public void OOPS()                               // что-то произошло

        {

            Console.WriteLine( "OOPS!" );

            if ( dels != null ) dels( this );     // оповещение наблюдателей

        }

    }

     class ObsA                                          // класс-наблюдатель

    {

        public void Do( object o )           // реакция на событие источника

        {

            Console.WriteLine( "Вижу, что OOPS!" );

        }

    }

     class ObsB                                          // класс-наблюдатель

    {

        public static void See( object o )   // реакция на событие источника

        {

            Console.WriteLine( "Я тоже вижу, что OOPS!" );

        }

    }

 

    class Class1

    {

        static void Main()

        {

            Subj s  = new Subj();                //  объект класса-источника

             ObsA o1 = new ObsA();                //                  объекты

            ObsA o2 = new ObsA();                //       класса-наблюдателя

             s.Register( new Del( o1.Do ) );      //      регистрация методов

            s.Register( new Del( o2.Do ) );      // наблюдателей в источнике

            s.Register( new Del( ObsB.See ) );   //  ( экземпляры делегата )

             s.OOPS();                            //    инициирование события

        }

    }

}

 

В источнике объявляется экземпляр делегата, в этот экземпляр заносятся методы тех объектов, которые хотят получать уведомление об изменении состояния источника. Этот процесс называется регистрацией делегатов. При регистрации имя метода добавляется к списку. При наступлении «часа Х» все зарегистрированные методы поочередно вызываются через делегат.

Результат работы программы:

OOPS!

Вижу, что OOPS!

Вижу, что OOPS!

Я тоже вижу, что OOPS!

 

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

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

public void UnRegister( Del d )                 // удаление делегата

{

    dels -= d;

}

 

Операции

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

С делегатами одного типа можно выполнять операции простого и сложного присваивания, например:

Del d1 = new Del( o1.Do );      // o1.Do

Del d2 = new Del( o2.Do );      // o2.Do

Del d3 = d1 + d2;               // o1.Do и o2.Do

d3 += d1;                       // o1.Do, o2.Do и o1.Do

d3 -= d2;                       // o1.Do и o1.Do

 

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

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

Передача делегатов в методы

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

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

Листинг 19.3. Передача делегата через список параметров

using System;

namespace ConsoleApplication1

{

    public delegate double Fun( double x );           // объявление делегата

     class Class1

    {

        public static void Table( Fun F, double x, double b )

        {

            Console.WriteLine( " ----- X ----- Y -----" );

            while (x <= b)

            {

               Console.WriteLine( "| {0,8:0.000} | {1,8:0.000} |", x, F(x));

               x += 1;

            }

            Console.WriteLine( " ---------------------" );

        }

         public static double Simple( double x )

        {

            return 1;

        }

               static void Main()

        {

            Console.WriteLine( " Таблица функции Sin " );

            Table( new Fun( Math.Sin ), -2, 2 );

             Console.WriteLine( " Таблица функции Simple " );

            Table( new Fun( Simple ), 0, 3 );

        }

    }

}

 

Результат работы программы:

Таблица функции Sin

----- X ----- Y -----

|   -2,000 |   -0,909 |

|   -1,000 |   -0,841 |

|    0,000 |    0,000 |

|    1,000 |    0,841 |

|    2,000 |    0,909 |

---------------------

Таблица функции Simple

----- X ----- Y -----

|    0,000 |    1,000 |

|    1,000 |    1,000 |

|    2,000 |    1,000 |

|    3,000 |    1,000 |

---------------------

 

Обратный вызов (callback) представляет собой вызов функции, передаваемой в другую функцию в качестве параметра. Рассмотрим рисунок 19.1. Допустим, в библиотеке описана функция А, параметром которой является имя другой функции. В вызывающем коде описывается функция с требуемой сигнатурой (В) и передается в функцию А. Выполнение функции А приводит к вызову В, то есть управление передается из библиотечной функции обратно в вызывающий код.

Механизм обратного вызова широко используется в программировании. Например, он реализуется во многих стандартных функциях Windows.

В среде Visual Studio 2005, использующей версию 2.0 языка C#, можно применять упрощенный синтаксис для делегатов. Первое упрощение заключается в том, что в большинстве случаев явным образом создавать экземпляр делегата не требуется, поскольку он создается автоматически по контексту. Второе упрощение заключается в возможности создания так называемых анонимных методов — фрагментов кода, описываемых непосредственно в том месте, где используется делегат:

        static void Main()

        {

            Console.WriteLine( " Таблица функции Sin " );

            Table( Math.Sin, -2, 2 );                         // упрощение 1

 

            Console.WriteLine( " Таблица функции Simple " );

            Table( delegate (double x ){ return 1; }, 0, 3 ); // упрощение 2

        }

    }

}

 

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

Лекция 18. События

Событие — это элемент класса, позволяющий ему посылать другим объектам уведомления об изменении своего состояния. При этом для объектов, являющихся наблюдателями события, активизируются методы-обработчики этого события. Обработчики должны быть зарегистрированы в объекте-источнике события. Таким образом, механизм событий формализует на языковом уровне паттерн «наблюдатель», который рассматривался в предыдущем разделе.

Механизм событий можно также описать с помощью модели «публикация — подписка»: один класс, являющийся отправителем (sender) сообщения, публикует события, которые он может инициировать, а другие классы, являющиеся получателями (receivers) сообщения, подписываются на получение этих событий.

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

                    описание делегата, задающего сигнатуру обработчиков событий;

                    описание события;

                    описание метода (методов), инициирующих событие.

Синтаксис события похож на синтаксис делегата:

[ атрибуты ] [ спецификаторы ] event тип имя_события

 

Для событий применяются спецификаторыnew, public, protected, internal, private, static, virtual, sealed, override, abstract и extern. Например, так же как и методы, событие может быть статическим (static), тогда оно связано с классом в целом, или обычным — в этом случае оно связано с экземпляром класса. Тип события — это тип делегата, на котором основано событие.

Пример описания делегата и соответствующего ему события:

public delegate void Del( object o );                 // объявление делегата

class A

{

    public event Del Oops;                             // объявление события

    ...

}

 

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

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

В листинге 19.4 приведен код из листинга 19.2, переработанный с использованием событий.

Листинг 19.4. Оповещение наблюдателей с помощью событий

using System;

namespace ConsoleApplication1

{

    public delegate void Del();                       // объявление делегата

 

    class Subj                                             // класс-источник

    {

        public event Del Oops;                         // объявление события

 

        public void CryOops()                 // метод, инициирующий событие

        {

            Console.WriteLine( "OOPS!" );

            if ( Oops != null ) Oops();

        }

    }

 

    class ObsA                                          // класс-наблюдатель

    {

        public void Do();                    // реакция на событие источника

        {

            Console.WriteLine( "Вижу, что OOPS!" );

        }

    }

 

    class ObsB                                          // класс-наблюдатель

    {

        public static void See()             // реакция на событие источника

        {

            Console.WriteLine( "Я тоже вижу, что OOPS!" );

        }

    }

 

    class Class1

    {

        static void Main()

        {

            Subj s  = new Subj();              //    объект класса-источника

 

            ObsA o1 = new ObsA();              //                    объекты

            ObsA o2 = new ObsA();              //         класса-наблюдателя

 

            s.Oops += new Del( o1.Do );        //                 добавление

            s.Oops += new Del( o2.Do );        //               обработчиков

            s.Oops += new Del( ObsB.See );     //                  к событию

 

            s.CryOops();                       //      инициирование события

        }

    }

}

 

Внешний код может работать с событиями единственным образом: добавлять обработчики в список или удалять их, поскольку вне класса могут использоваться только операции += и -=. Тип результата этих операций — void, в отличие от операций сложного присваивания для арифметических типов.

Внутри класса, в котором описано событие, с ним можно обращаться, как с обычным полем, имеющим тип делегата: использовать операции отношения, присваивания и т. д. Значение события по умолчанию — null.

В библиотеке .NET описано огромное количество стандартных делегатов, предназначенных для реализации механизма обработки событий. Большинство этих классов оформлено по одним и тем же правилам:

                    имя делегата заканчивается суффиксом EventHandler;

                    делегат получает два параметра: первый параметр задает источник события и имеет тип object; второй параметр задает аргументы события и имеет тип EventArgs или производный от него.

Если обработчикам события требуется специфическая информация о событии, то для этого создают класс, производный от стандартного класса EventArgs, и добавляют в него необходимую информацию. Если делегат не использует такую информацию, можно обойтись стандартным классом делегата System.EventHandler.

Имя обработчика события принято составлять из префикса On и имени события. В листинге 19.5 приведен пример из листинга 19.4, оформленный в соответствии со стандартными соглашениями .NET.

Листинг 19.5. Использование стандартного делегата EventHandler

using System;

namespace ConsoleApplication1

{

    class Subj

    {

        public event EventHandler Oops;

 

        public void CryOops()

        {

            Console.WriteLine( "OOPS!" );

            if ( Oops != null ) Oops( this, null );

        }

    }

 

    class ObsA

    {

        public void OnOops( object sender, EventArgs e )

        {

            Console.WriteLine( "Вижу, что OOPS!" );

Информация о работе Программирование