Знакомство с Microsoft .NET Framework. Часть 2Источник: КомпьютерПресс, 7'2002 Алексей Федоров
ОглавлениеВ предыдущей статье мы начали знакомство с Microsoft .NET Framework - ключевым компонентом Microsoft .NET, представляющим собой платформу для создания, внедрения и выполнения Web-сервисов и приложений. Мы рассмотрели основные компоненты Microsoft .NET Framework и кратко описали их назначение. Мы также начали более подробное рассмотрение Common Language Runtime (CLR) - среды выполнения .NET-приложений. В этой части статьи мы продолжим разговор о Common Language Runtime и остановимся на Common Type System. Common Type System (CTS) - это последний компонент Common Language Runtime, который мы рассмотрим в данной статье. CTS определяет типы, поддерживаемые Common Language Runtime. Типы можно условно разделить на две большие группы: данные со значениями (value types) и ссылочные типы (reference types). В каждой группе существуют подтипы (см. рис. 1). Данные со значениями описывают значения, представляемые последовательностью байтов. Для таких типов существует понятие идентичности. Например, 32-битное целочисленное значение занимает определенную последовательность байтов. Два 32-битных целочисленных значения идентичны, если они содержат одно и то же число. К данным со значениями также относятся встроенные типы, рассматриваемые ниже. Ссылочные типы служат для описания значений, представляемых местонахождением последовательности байтов. Ссылочные типы подразделяются на три категории:
Составной частью Common Type System являются встроенные типы (табл. 1), непосредственно поддерживаемые виртуальной исполняющей системой (Virtual Execution System, VES). Таблица 1
Мы рассмотрим типы и их подтипы более подробно, но прежде отметим, что в основе всех типов, как, впрочем, и в основе всей иерархии классов Microsoft .NET Framework, лежит класс System.Object. Для всех типов, которые будут рассмотрены ниже, этот класс, в частности, предоставляет методы, перечисленные в табл. 2. Таблица 2
Далее мы сможем убедиться, что все типы так или иначе наследуют от System.Object. Начнем с данных со значениями. Как мы отметили выше, к данным со значением относятся как встроенные типы так и определяемые пользователями. Все встроенные типы базируются на классе System.ValueType, который является непосредственным наследником System.Object. Каждый класс, реализующий встроенный тип, поддерживает определенный набор интерфейсов, которые используются для реализации тех или иных операций, а также уникальные для данного типа свойства и методы. Например, класс Boolean поддерживает интерфейсы IConvertible (для выполнения операций преобразования) и IComparable (для выполнения операций сравнения), а также содержит такие свойства, как FalseString и TrueString. Кроме того, класс Byte поддерживает интерфейсы IConvertible и IComparable, а также интерфейс IFormattable, используемый для преобразования значений в их строчное представление. Двигаясь дальше по списку наследников класса System. ValueType, мы находим класс Char, который поддерживает такие методы, как IsDigit, IsLetter, IsLetterOrDigit, IsNumber и т.п., существенно облегчающие работу с символами. Классы, представляющие целочисленные типы - Int16, Int32, Int64, IntPtr, UInt16, UInt32, UInt64 и UIntPtr, содержат константы MinValue и MaxValue, задающие минимальное и максимальное число, которое может содержаться в данном типе, а также метод Parse, используемый для преобразования строки, состоящей из набора цифр, в соответствующий тип данных. Типы с плавающей точкой - Single и Double - содержат свойства Epsilon (минимальное положительное число данного типа), MinValue (минимально допустимое число), MaxValue (максимально допустимое число), Nan (константа Not-A-Number, не число), NegativeInfinity (константа, описывающая отрицательную бесконечность) и PositiveInfinity (константа, описывающая положительную бесконечность), а также методы IsInfinity, IsNan, IsNegativeInfinity и IsPositiveInfinity, используемые для проверки хранимых в переменной данного типа значений. Среди разнообразия типов, которые могут определять пользователи, отметим перечисления, создающиеся на базе класса System. Enum. Этот класс поддерживает интерфейсы IComparable, IFormattable и IConvertible, а также методы для получения имен констант (GetName, GetNames), получения значений (GetValues), определения типа элементов перечисления (GetUnderlyingType) и ряд других. Module Module1 ' Sub Main() Dim GeoShapes As New Shapes() End Sub Структуры позволяют создавать новые типы данных путем объединения нескольких существующих типов. Структуры могут содержать методы и события, но в отличие от классов, которые мы рассмотрим ниже, структуры не поддерживают наследования и инициализации полей. Рассмотрим небольшой пример. Создадим структуру TPoint, у которой будет два поля: целочисленные члены X и Y. В коде нашей программы, написанной на Visual Basic.NET, создадим переменную типа TPoint, инициализируем ее поля и выведем на экран их значения. Как это сделать, показано ниже: Module Module1 ' Sub Main() ' With MyPoint Теперь скопируем структуру в объектную переменную и изменим значения ее полей. Поскольку структура скопирована, а не передана по ссылке, оригинальные значения не изменяются: Dim MyPoint As TPoint With MyPoint NewPoint = MyPoint End Sub По сравнению со структурами классы предоставляют большую гибкость. Мы рассмотрим классы в следующем разделе. Вторая группа типов в Common Type System - это ссылочные типы. Среди них есть объектные, интерфейсные и указательные типы. Рассмотрение ссылочных типов начнем с классов. Классы - это структуры, определяющие операции, которые могут выполнять объекты, а также значения, которые объекты могут хранить. Функциональность объектов (экземпляров класса) доступна через поля и методы, а также через события. Продолжим наш пример. Теперь мы преобразуем созданную выше структуру в класс, который также будет иметь два целочисленных свойства. Для установки и получения значений этих свойств мы должны использовать ключевое слово Property и новые в VB.NET конструкции языка - Get/End Get и Set/End Set. Мы также создаем конструктор для нашего класса. Реализация класса TPoint показана ниже: Imports System Module Module1 ' Sub Main() With MyPoint End Sub End Module Здесь следует обратить внимание на то, что внутренние имена свойств класса (в нашем примере ptX и ptY) отличаются от имен, "видимых" вне класса (в нашем примере X и Y). Методы служат для реализации функциональности классов. Рассмотрим это на примере. Расширим наш класс и реализуем метод SetXY, который будет устанавливать новые значения свойств X и Y. Для этого добавим в описание класса следующий код: Public Sub SetXY(ByVal newX As Integer, ByVal newY As Integer) и изменим код нашей программы: Sub Main() With MyPoint End Sub И, наконец, последним элементом, который может содержаться в классах, создаваемых пользователями, является событие. Как было отмечено выше, события служат для асинхронных объявлений об изменениях в объектах. В нашем примере таким изменением в объекте будет задание новых значений свойств X и Y. Реализацию события начнем с задания его имени, сопровождаемого ключевым словом Event. Далее в код, который является причиной возникновения события (в нашем примере это код, устанавливающий новые значения свойств), добавляем ключевое слово RaiseEvent и указываем название возникающего события: Public Class TPoint Event PTChanged() ' Проверить работу нашего класса, который теперь имеет свойства, методы и события, можно с помощью небольшого клиентского приложения. Оно содержит кнопку, список и панель. При каждом нажатии кнопки мыши на панели мы вызываем метод SetXY нашего класса Tpoint, в результате чего происходит событие, которое регистрируется нашим обработчиком события в списке. Вот код клиентского приложения (для простоты класс TPoint реализован в отдельной библиотеке классов): Imports ClassLibrary1 Dim WithEvents MyPoint As TPoint Private Sub Button1_Click(ByVal sender As System.Object, _ Private Sub Panel1_MouseDown(ByVal sender As Object, _ Private Sub MyPoint_PTChanged() Handles MyPoint.PTChanged End Class Теперь объект MyPoint типа TPoint описан с использованием ключевого слова WithEvents. Это означает, что класс поддерживает события и мы можем создавать их обработчики. На рис. 2 показан пример работы нашей программы. Обратите внимание на то, что в списке регистрируются парные координаты - X, Y, X, Y1, X1, Y1 и т.п. Это происходит из-за того, что событие PTChanged возникает и при изменении значения свойства X, и при изменении значения свойства Y. Таким образом, если одновременно изменяются два свойства, возникают два события. Чтобы исправить логику, достаточно удалить код, генерирующий события, из описаний свойств и добавить его в реализацию метода SetXY, как это показано ниже: Public Sub SetXY(ByVal newX As Integer, ByVal newY As Integer) Говоря о событиях, следует упомянуть о делегатах - классе на базе System.Delegate, который содержит ссылку на метод. Таким образом, делегат - это указатель на функцию или косвенно-вызываемая функция. Чаще всего делегаты используются для описания обработчиков событий или для создания косвенно-вызываемых функций. Ниже показано, как изменить код предыдущего примера так, чтобы обработчик события стал делегатом и подключался в момент выполнения программы: Dim MyPoint As TPoint Private Sub Button1_Click(ByVal sender As System.Object, _ Private Sub Panel1_MouseDown(ByVal sender As Object, _ Private Sub MyPoint_PTChanged() Такой подход позволяет нам создавать обработчики событий для более чем одного события, динамически переключать и отключать обработчики событий и т.п. Массивы задаются описанием типа элементов, числа размерностей, и нижней и верхней границ каждой размерности и могут содержать только данные указанного типа. Каждый элемент массива - это объект. массив базируется на классе System.Array, описывающем базовую функциональность массивов. Этот класс поддерживает такие интерфейсы, как ICloneable (реализует возможность создания еще одного экземпляра класса со значениями существующего экземпляра), IList (реализует возможность индивидуальной индексации коллекции объектов), ICollection (реализует возможность задания размеров, создания перечислителей и методов синхронизации) и IEnumerable (поддерживает итерацию по элементам коллекции). Ниже приведены базовые свойства (табл. 3) и методы (табл. 4), поддерживаемые массивами. Таблица 3
Таблица 4
В следующем примере показаны некоторые базовые операции с массивами: Sub Main() With IntArray End Sub Ниже показано, как использовать методы GetValue и SetValue для получения значений элементов массива и изменения этих значений: Console.WriteLine(vbCrLf & "Original Array:") Console.WriteLine(vbCrLf & "Modified Array:") Интерфейсы служат для описания функциональности, которая должна быть реализована тем или иным классом, но они не содержат кода, реализующего данную функциональность. Поскольку интерфейс не содержит кода, его нельзя унаследовать - вместо этого вы наследуете класс, реализующий тот или иной интерфейс. Интерфейсы широко используются в библиотеке классов Microsoft .NET. Мы уже приводили несколько примеров классов, в которых реализуются такие интерфейсы, как IConvertible (для выполнения операций преобразования) и IComparable (для выполнения операций сравнения)). Полный список интерфейсов, определенных в библиотеке классов Microsoft .NET, представлен в документации к Microsoft .NET Framework SDK. Common Language Runtie поддерживает три типа указателей. К первому типу относятся управляемые указатели (managed poiters), генерируемые для аргументов методов, которые передаются по ссылке. Это единственный тип указателей, совместимый со спецификацией Common Language Runtime. К двум другим типам относятся неуправляемые указатели (unmanaged pointers) и неуправляемые указатели на функции (unmanaged function pointers). Так как неуправляемые указатели не совместимы с Common Language Runtime, их реализация и поддержка зависят от выбранного языка программирования. Common Language Runtime поддерживает две операции над управляемыми указателями: получение значения, хранимого по указателю, и запись значения по адресу указателя. Отметим, что хотя указатели являются ссылочными типами, значение типа "указатель" не является объектом, а следовательно, определить точный тип такого значения невозможно. Мы ознакомились с одним из основных компонентов Microsoft .NET Framework - средой выполнения Common Language Runtime, рассмотрели такие темы, как исполняемые файлы и метаданные, кратко поговорили о Microsoft Intermediate Language (MSIL), Just-In-Time Compiler, узнали о назначении сборок и глобального кэша сборок (Global Assembly Cache). Затем мы подробно рассмотрели систему типов Common Type System и основные группы типов - данные со значениями и ссылочные типы. Отметим, что поддержка типов на уровне Common Language Runtime обеспечивает простую интеграцию кода, написанного на разных языках программирования. Вопрос о выборе языка программирования - будь то Visual Basic .NET, C# или какой-либо другой - не является первостепенным, поскольку все языки, поддерживающие спецификацию Common Language Runtime, обеспечивают доступ к рассмотренным нами типам. Вопрос лишь в синтаксических особенностях того или иного языка. Проиллюстрируем вышесказанное. Рассказывая о классах, свойствах, методах и событиях, мы создали VB.NET-класс, реализующий объект TPoint, с двумя свойствами - X и Y и методом SetXY, который позволяет изменять значения этих свойств. При изменении значений свойств X и Y возникает событие PTChanged. Создадим клиентское приложение на C#, которое будет использовать наш VB.NET-класс, и в нем реализуем обработчик события PTChanged. Ниже приведен фрагмент кода, иллюстрирующий необходимые действия: public class Form1 : System.Windows.Forms.Form public Form1() this.MyPoint = new ClassLibrary1.TPoint(); } ... private void button1_Click(object sender, System.EventArgs e) private void button3_Click(object sender, System.EventArgs e) В данном случае необходимо лишь создать Windows-приложение на C#, подключить ссылку на библиотеку классов, в которой описан класс, реализующий объект TPoint, и описать объект типа TPoint: private ClassLibrary1.TPoint MyPoint; Теперь вызовем конструктор объекта TPoint и опишем новый обработчик событий: this.MyPoint = new ClassLibrary1.TPoint(); Сам обработчик событий реализован в следующем методе: void MyPoint_PTChanged(object sender, System.EventArgs e) Те, кто когда-либо пробовал использовать в Visual Basic библиотеки для C или интегрировать код на Delphi с кодом на VB, смогут по достоинству оценить изменения, привнесенные Common Language Runtime. В следующей статье мы начнем ознакомление с библиотекой классов, рассмотрим основные составляющие ее пространства имен и классы, а также приведем некоторые примеры их использования. |