Технология .NET предназначена для разработки приложений для систем семейства Windows, хотя в перспективе компания Microsoft обещает продвинуть ее и на другие операционные платформы. Основу этой технологии составляет библиотека классов Framework Class Library, которая в настоящее время включает в себя несколько тысяч классов, с помощью которых можно создавать практически любые типы Windows-приложений.
Одной из особенностей процесса разработки .NET-приложений является языковая независимость (относительная, конечно): исходный текст можно написать на любом языке, поддерживающем данную технологию (C#, J#, VB, C++), после чего соответствующий компилятор переводит этот текст в универсальное промежуточное представление. Это представление описывается с помощью специального языка IL (Intermediate Language), немного похожего на язык ассемблера. После этого отдельные IL-модули могут объединяться в так называемую сборку (assembly), которая представляет собой единицу распространения программы. Интересно, что сборка помимо кода на IL-языке содержит метаданные с полным описанием используемых в программе типов и классов. При запуске программы на выполнение IL-код переводится на машинный язык конкретного процессора и выполняется. Этими процессами управляет специальное средство .NET-технологии – так называемая Common Language Runtime (CLR, общеязыковая среда выполнения).
Компонентная модель платформы .NET аналогична рассмотренным ранее моделям языков Java и Delphi Pascal. При описании класса вводятся закрытые поля данных и открытые свойства для определения методов доступа к этим полям. Описание свойства должно включать объявление одного или двух методов доступа с обязательными именами get и set.
Например:
publicclass MyClass
{ private int myfield; // закрытое поле
public int MyField // соответствующее свойство
{ get { return myfield; } // метод доступа
{ set { myfield = value; } // метод доступа
}
. . . другие поля и их свойства . . .
}
Аналогично другим языкам, механизм свойств позволяет организовать более строгий и контролируемый доступ к значениям внутренних свойств объектов.
Как уже отмечалось выше, компонент как строительный блок приложения должен уметь реагировать на определенные события. Обработка событий в .NET-приложениях основана на использовании указателей на функции-обработчики, но по сравнению с пакетом Delphi реализована более надежным способом – с помощью специальных классов и объектов, называемых делегатами (delegate). Базовым классом для реализации делегатов является класс MulticastDelegate, в котором вводится возможность хранения любого числа указателей на функции и все необходимые для этого методы. На основе этого класса создаются пользовательские дочерние классы, объекты которых и выполняют всю работу по обращению к необходимым функциям. Поскольку все вызовы таких функций выполняются динамически при работе приложения, то без использования среды выполнения CLR обойтись невозможно.
Класс-делегат описывается неявно с помощью служебных слов, например (в языке C#) – с помощью директивы delegate. Синтаксис описания делегатов отличается от привычного описания классов, поскольку часть работы возложена на компилятор. При описании класса-делегата указывается прототип метода, на который будут ссылаться объекты-экземпляры этого класса:
delegate void MyDelegat (string st);
После этого надо создать объект-делегат с помощью неявного конструктора, передав ему в качестве параметра имя метода, на который должен ссылаться объект-делегат:
MyDelegat myDel = new MyDelegat (myObj.MyMetod);
Здесь myObj – объект класса, в котором реализован пользовательский метод с именем MyMetod. Пусть для наглядности этот метод MyMetod просто лишь выводит на экран текстовую строку, передаваемую ему через входной параметр st. Тогда для вызова этого метода с помощью делегата достаточно оформить следующую простую конструкцию:
myDel (“ Hello from delegate”);
Конечно, в данном примере использование делегатов кажется надуманным, поскольку метод MyMetod можно вызвать непосредственно. Однако для реализации обратных вызовов, возникающих при выполнении программы этот механизм весьма удобен. Именно по этой причине он и используется при обработке событий. При этом важным моментом в механизме делегатов становится возможность объединения объектов-делегатов в списки. Для добавления и удаления элементов в эти списки можно использовать методы Combine и Remove базового класса MulticastDelegate. Для удобства использования этих методов в языке C# переопределены операторы += и -= . Вместо них компилятор подставляет вызовы методов Combine и Remove. Например, если вместе с методом MyMetod объявить другой метод MyMetod2 с такими же параметрами (в данном случае – с одним параметром строкового типа), то его можно добавить в цепочку делегатов с помощью следующей конструкции:
myDel += myObj.MyMetod2;
После этого указанное выше обращение к делегату приведет к вызову уже двух разных методов.
Обработка событий встраивается в компоненты следующим образом. Прежде всего, в классе компонента объявляется закрытое поле-делегат, через которое должна устанавливаться связь с объектом-делегатом. Потом в классе вводится специальное открытое свойство с двумя методами, которые пользователи компонента могут использовать для добавления и удаления событий в списке вызова делегата. Поскольку эти операции являются достаточно стандартными, в язык С# введена специальная директива event, с помощью которой в классе компонента и вводятся необходимые составляющие:
class MyComponent: ParentComponent
{ publicevent MyDelegat MyEvent;
. . . . . . . . . };
После этого пользователи компонента должны создать его экземпляры и добавить обработчики события MyEvent следующим образом:
MyComponent myComp = new MyComponent ( );
myComp += new MyDelegat (myComp.MyMetod);
В заключение отметим, что для создания полноценных компонентов необходимо придерживаться ряда простых рекомендаций по оформлению делегатов и методов-обработчиков событий [15].