СТАТЬЯ |
08.05.02
|
Знакомство с Microsoft .NET Framework
Часть 2. Common Language Runtime
© Алексей Федоров
Материал опубликован в КомпьютерПресс
№1'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).
Мы рассмотрим типы и их подтипы более подробно, но прежде отметим, что в основе всех типов, как, впрочем, и в основе всей иерархии классов Microsoft .NET Framework, лежит класс System.Object. Для всех типов, которые будут рассмотрены ниже, этот класс, в частности, предоставляет методы, перечисленные в табл. 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) и ряд других.
В следующем примере показано, как создать простое перечисление и воспользоваться
методами GetNames и GetValues для получения списка имен всех его полей и их
значений:
Module Module1
'
' Геометрические фигуры
'
Public Enum Shapes
None = 0
Circle = 1
Rectangle = 2
Ellipse = 3
Triangle = 4
End Enum
Sub Main()
Dim GeoShapes As
New Shapes()
Dim Names As String()
Dim Values() As
Integer
Dim I As Integer
Names = GeoShapes.GetNames(GeoShapes.GetType)
Values = GeoShapes.GetValues(GeoShapes.GetType)
Console.WriteLine()
For I = 0 To Names.GetUpperBound(0)
Console.WriteLine(vbTab
& Names(I) & "=" & _
CType(Values(I),
Integer).ToString("G"))
Next
Console.WriteLine()
End Sub
End Module
Структуры позволяют создавать новые типы данных путем объединения нескольких существующих типов. Структуры могут содержать методы и события, но в отличие от классов, которые мы рассмотрим ниже, структуры не поддерживают наследования и инициализации полей.
Рассмотрим небольшой пример. Создадим структуру TPoint, у которой будет два поля: целочисленные члены X и Y. В коде нашей программы, написанной на Visual Basic.NET, создадим переменную типа TPoint, инициализируем ее поля и выведем на экран их значения. Как это сделать, показано ниже:
Module Module1
'
' Структура TPoint содержит два целочисленных члена
'
Structure TPoint
Public X As Integer
Public Y As Integer
End Structure
Sub Main()
'
' Переменная MyPoint - пользовательского типа
'
Dim MyPoint As TPoint
'
' Инициализация переменной
'
With MyPoint
.X
= 100
.Y
= 150
'
' Вывод значений
'
Console.WriteLine("
X should be 100" & " X=" & .X)
Console.WriteLine("
Y should be 150" & " Y=" & .Y)
End With
End Sub
End Module
Теперь скопируем структуру в объектную переменную и изменим значения ее полей. Поскольку структура скопирована, а не передана по ссылке, оригинальные значения не изменяются:
Dim MyPoint As TPoint
Dim NewPoint
With MyPoint
.X = 100
.Y = 150
End With
NewPoint = MyPoint
With NewPoint
.X = 200
.Y = 50
End With
Console.WriteLine("NewPoint X, Y=" & NewPoint.X & ",
" & NewPoint.Y)
Console.WriteLine("MyPoint X, Y=" & MyPoint.X & ", "
& MyPoint.Y)
End Sub
По сравнению со структурами классы предоставляют большую гибкость. Мы рассмотрим
классы в следующем разделе.
Завершая наше знакомство с данными со значениями, напомним, что они описывают
значения, представляемые последовательностью байтов. Многие встроенные типы,
перечисленные в табл. 1, относятся к этой группе. Данные
со значениями часто располагаются на стеке - они могут быть локальными переменными,
параметрами и значениями, возвращаемыми функциями. Таким образом, данные со
значениями не должны занимать много места - в общем случае они занимают не более
12-16 байтов.
Вторая группа типов в Common Type System - это ссылочные типы. Среди них есть
объектные, интерфейсные и указательные типы. Рассмотрение ссылочных типов начнем
с классов.
В общем случае классы и объекты могут содержать поля, свойства, методы и события.
Строго говоря, свойства и события являются методами. Свойства - это не более
чем синтаксические конструкции, заменяющие обращения к соответствующим методам
Set и Get. События служат для асинхронных объявлений об изменениях в объектах.
Клиенты предоставляют специальные методы - обработчики событий, которые вызываются
при возникновении данного события.
Классы - это структуры, определяющие операции, которые могут выполнять объекты, а также значения, которые объекты могут хранить. Функциональность объектов (экземпляров класса) доступна через поля и методы, а также через события.
Продолжим наш пример. Теперь мы преобразуем созданную выше структуру в класс, который также будет иметь два целочисленных свойства. Для установки и получения значений этих свойств мы должны использовать ключевое слово Property и новые в VB.NET конструкции языка - Get/End Get и Set/End Set. Мы также создаем конструктор для нашего класса. Реализация класса TPoint показана ниже:
Imports System
Module Module1
'
' Класс TPoint содержит два целочисленных свойства
'
Public Class TPoint
Dim ptX As Integer
' X
Dim ptY As Integer
' Y
'
' Get и Set для свойства ptX
'
Public Property
X() As Integer
Get
Return
ptX
End
Get
Set(ByVal
Value As Integer)
ptX
= Value
End
Set
End Property
'
' Get и Set для свойства ptY
'
Public Property
Y() As Integer
Get
Return
ptY
End
Get
Set(ByVal
Value As Integer)
ptY
= Value
End
Set
End Property
'
' Конструктор
'
Public Sub New()
ptX
= 0
ptY
= 0
End Sub
End Class
Sub Main()
Dim MyPoint As New
TPoint()
'
' Инициализация объекта
'
With MyPoint
.X
= 100
.Y
= 150
'
' Вывод значений
'
Console.WriteLine("
X should be 100" & " X=" & .X)
Console.WriteLine("
Y should be 150" & " Y=" & .Y)
End With
End Sub
End Module
Здесь следует обратить внимание на то, что внутренние имена свойств класса (в нашем примере ptX и ptY) отличаются от имен, "видимых" вне класса (в нашем примере X и Y).
Методы служат для реализации функциональности классов. Рассмотрим это на примере. Расширим наш класс и реализуем метод SetXY, который будет устанавливать новые значения свойств X и Y. Для этого добавим в описание класса следующий код:
Public Sub SetXY(ByVal newX As Integer, ByVal newY As Integer)
X = newX
Y = newY
End Sub
и изменим код нашей программы:
Sub Main()
Dim MyPoint As New
TPoint()
'
' Инициализация объекта
'
With MyPoint
.SetXY(100, 150)
'
' Вывод значений
'
Console.WriteLine("
X should be 100" & " X=" & .X)
Console.WriteLine("
Y should be 150" & " Y=" & .Y)
End With
End Sub
И, наконец, последним элементом, который может содержаться в классах, создаваемых пользователями, является событие. Как было отмечено выше, события служат для асинхронных объявлений об изменениях в объектах. В нашем примере таким изменением в объекте будет задание новых значений свойств X и Y.
Реализацию события начнем с задания его имени, сопровождаемого ключевым словом Event. Далее в код, который является причиной возникновения события (в нашем примере это код, устанавливающий новые значения свойств), добавляем ключевое слово RaiseEvent и указываем название возникающего события:
Public Class TPoint
Dim
ptX As Integer ' X
Dim
ptY As Integer ' Y
Event
PTChanged()
'
'
Get и Set для свойства ptX
'
Public
Property X() As Integer
Get
Return
ptX
End
Get
Set(ByVal
Value As Integer)
ptX
= Value
RaiseEvent
PTChanged()
End
Set
End Property
'
' Get и Set для свойства ptY
'
Public Property Y() As Integer
Get
Return
ptY
End
Get
Set(ByVal
Value As Integer)
ptY
= Value
RaiseEvent
PTChanged()
End
Set
End Property
'
' Конструктор
'
Public Sub New()
ptX
= 0
ptY
= 0
End Sub
Public Sub SetXY(ByVal newX As Integer, ByVal newY As Integer)
X
= newX
Y
= newY
End Sub
End Class
Проверить работу нашего класса, который теперь имеет свойства, методы и события, можно с помощью небольшого клиентского приложения. Оно содержит кнопку, список и панель. При каждом нажатии кнопки мыши на панели мы вызываем метод SetXY нашего класса Tpoint, в результате чего происходит событие, которое регистрируется нашим обработчиком события в списке.
Вот код клиентского приложения (для простоты класс TPoint реализован в отдельной библиотеке классов):
Imports ClassLibrary1
Public Class Form1
Inherits
System.Windows.Forms.Form
Dim
WithEvents MyPoint As TPoint
Private
Sub Button1_Click(ByVal sender As System.Object, _
ByVal
e As System.EventArgs) Handles Button1.Click
MyPoint
= New TPoint()
End
Sub
Private Sub Panel1_MouseDown(ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs)
_
Handles Panel1.MouseDown
MyPoint.SetXY(e.X,
e.Y)
End Sub
Private Sub MyPoint_PTChanged() Handles MyPoint.PTChanged
ListBox1.Items.Add(MyPoint.X
& ", " & MyPoint.Y)
End Sub
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)
X
= newX
Y
= newY
RaiseEvent
PTChanged()
End Sub
Говоря о событиях, следует упомянуть о делегатах - классе на базе System.Delegate, который содержит ссылку на метод. Таким образом, делегат - это указатель на функцию или косвенно-вызываемая функция. Чаще всего делегаты используются для описания обработчиков событий или для создания косвенно-вызываемых функций.
Ниже показано, как изменить код предыдущего примера так, чтобы обработчик события стал делегатом и подключался в момент выполнения программы:
Dim MyPoint As TPoint
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
MyPoint
= New TPoint()
AddHandler
MyPoint.PTChanged, AddressOf MyPoint_PTChanged
End Sub
Private Sub Panel1_MouseDown(ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs)
_
Handles Panel1.MouseDown
MyPoint.SetXY(e.X,
e.Y)
End Sub
Private Sub MyPoint_PTChanged()
ListBox1.Items.Add(MyPoint.X
& ", " & MyPoint.Y)
End Sub
Такой подход позволяет нам создавать обработчики событий для более чем одного события, динамически переключать и отключать обработчики событий и т.п.
Массивы задаются описанием типа элементов, числа размерностей, и нижней и верхней границ каждой размерности и могут содержать только данные указанного типа. Каждый элемент массива - это объект. массив базируется на классе System.Array, описывающем базовую функциональность массивов. Этот класс поддерживает такие интерфейсы, как ICloneable (реализует возможность создания еще одного экземпляра класса со значениями существующего экземпляра), IList (реализует возможность индивидуальной индексации коллекции объектов), ICollection (реализует возможность задания размеров, создания перечислителей и методов синхронизации) и IEnumerable (поддерживает итерацию по элементам коллекции). Ниже приведены базовые свойства (табл. 3) и методы (табл. 4), поддерживаемые массивами.
В следующем примере показаны некоторые базовые операции с массивами:
Sub Main()
Dim IntArray() As Integer = {0, 1, 2, 3, 4, 5}
With IntArray
Console.WriteLine(vbTab
& "Type = " & _
.GetType.ToString)
Console.WriteLine(vbTab
& "IsFixedSize = " & .IsFixedSize)
Console.WriteLine(vbTab
& "Length = " & .Length)
Console.WriteLine(vbTab
& "Rank = " & .Rank)
Console.WriteLine(vbTab
& "LowerBound = " & _
.GetLowerBound(0))
Console.WriteLine(vbTab
& "UpperBound = " & _
.GetUpperBound(0))
End With
End Sub
Ниже показано, как использовать методы GetValue и SetValue для получения значений элементов массива и изменения этих значений:
Console.WriteLine(vbCrLf & "Original Array:")
For I = .GetLowerBound(0) To .GetUpperBound(0)
Console.WriteLine(vbTab
& "Array(" & I & ")" & " = " &
.GetValue(I))
.SetValue(.GetValue(I)
* 10, I)
Next
Console.WriteLine(vbCrLf & "Modified Array:")
For I = .GetLowerBound(0) To .GetUpperBound(0)
Console.WriteLine(vbTab
& "Array(" & I & ")" & " = " &
.GetValue(I))
Next
Интерфейсы служат для описания функциональности, которая должна быть реализована тем или иным классом, но они не содержат кода, реализующего данную функциональность. Поскольку интерфейс не содержит кода, его нельзя унаследовать - вместо этого вы наследуете класс, реализующий тот или иной интерфейс.
Интерфейсы широко используются в библиотеке классов 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
{
...
private
ClassLibrary1.TPoint MyPoint;
public
Form1()
{
...
this.MyPoint
= new ClassLibrary1.TPoint();
MyPoint.PTChanged
+=
new
System.EventHandler(this.MyPoint_PTChanged);
}
...
private void button1_Click(object sender, System.EventArgs
e)
{
MyPoint.SetXY(10,
150);
}
private void button2_Click(object sender, System.EventArgs
e)
{
MyPoint.SetXY(100,
250);
}
private void button3_Click(object sender, System.EventArgs
e)
{
MyPoint.SetXY(50,
100);
}
void MyPoint_PTChanged(object sender, System.EventArgs
e)
{
MessageBox.Show("X="
+ MyPoint.X.ToString("G") +
",
Y=" + MyPoint.Y.ToString("G"),
"Changed",
MessageBoxButtons.OK);
}
}
}
В данном случае необходимо лишь создать Windows-приложение на C#, подключить ссылку на библиотеку классов, в которой описан класс, реализующий объект TPoint, и описать объект типа TPoint:
private ClassLibrary1.TPoint MyPoint;
Теперь вызовем конструктор объекта TPoint и опишем новый обработчик событий:
this.MyPoint = new ClassLibrary1.TPoint();
MyPoint.PTChanged += new
System.EventHandler(this.MyPoint_PTChanged);
Сам обработчик событий реализован в следующем методе:
void MyPoint_PTChanged(object sender, System.EventArgs e)
{
MessageBox.Show(
"X="
+ MyPoint.X.ToString("G") + ", Y=" +
MyPoint.Y.ToString("G"),
"Changed",
MessageBoxButtons.OK);
}
Те, кто когда-либо пробовал использовать в Visual Basic библиотеки для C или интегрировать код на Delphi с кодом на VB, смогут по достоинству оценить изменения, привнесенные Common Language Runtime.
В следующей статье мы начнем ознакомление с библиотекой классов, рассмотрим основные составляющие ее пространства имен и классы, а также приведем некоторые примеры их использования.
Дополнительная информация
Дополнительную информацию Вы можете получить в компании Interface Ltd.
Обсудить на форуме
Microsoft
Отправить
ссылку на страницу по e-mail
Interface Ltd. Отправить E-Mail http://www.interface.ru |
|
Ваши
замечания и предложения отправляйте
автору По техническим вопросам обращайтесь к вебмастеру Документ опубликован: 08.05.02 |