Основная цель этой книги — дать читателю прочные знания синтаксиса и семантики C#, а также разобрать особенности архитектуры .NET. После ее прочтения вы познакомитесь со всеми основными областями, охваченными библиотекой базовых классов C#. Для приобретения практических навыков книга содержит множество примеров, иллюстрирующих излагаемый материал. Для работы с книгой не нужен какой-либо предварительный опыт работы с C# и платформой .NET, однако при ее написании авторы ориентировались на тех разработчиков, которые уже имеют опыт работы с одним из современны
х языков программирования (C++, Visual Basic, Java или каким-либо другим).
Содержание
Предисловие
Благодарности
Глава 1. Философия .NET
Современное состояние дел
Как живут программисты, использующие Win32/C
Как живут программисты, использующие C++/MFC
Как живут программисты, использующие Visual Basic
Как живут программисты, использующие Java
Как живут COM-программисты
Как живут программисты, использующие Windows DNA
Решение .NET
Строительные блоки .NET (CLR, CTS и CLS)
Библиотека базовых классов .NET
Преимущества C#
Языки программирования .NET
Обзор двоичных файлов .NET ("сборки")
Сборки из одного и нескольких файлов
Роль Microsoft Intermediate Language
Преимущества IL
Роль метаданных
Простой пример метаданных
Компиляция IL в платформенно-зависимые инструкции
Типы .NET и пространства имен .NET
Основы Common Language Runtime - среды выполнения .NET
Стандартная система типов CTS
Классы CTS
Структуры CTS
Интерфейсы CTS
Члены типов CTS
Перечисления CTS
Делегаты CTS
Встроенные типы данных CTS
Основы CLS
Работа с пространствами имен
Важнейшие пространства имен .NET
Использование пространств имен в коде приложения
Обращения к внешним сборкам
Как получить дополнительную информацию о пространствах имен и типах
ILDasm.exe
Выгрузка в файл иерархии типов и членов сборки
Выгрузка в файл вместе с инструкциями IL
Просмотр метаданных типов
Web-приложение ClassViewer
Графическое приложение WinCV
Создание приложений C# с использованием компилятора командной строки
Ссылки на внешние сборки
Компиляция нескольких исходных файлов
Создание приложений C# с использованием интегрированной среды разработки Visual Studio.NET
Начало работы со средой Visual Studio.NET
Окно Solution Explorer
Окно Properties
Экономное размещение кода на экране
Ссылки на внешние сборки
Отладка в Visual Studio.NET
Работа с окном Server Explorer
Средства для работы с XML
Поддержка диаграмм UML
Утилита Object Browser
Средства для работы с базами данных
Встроенная справка
Подведение итогов
Глава 2. Основы языка C#
Анатомия класса C#
Как еще можно объявить метод Main()
Обработка параметров командной строки
Создание объектов: конструкторы
Будет ли происходить утечка памяти?
Композиция приложения C#
Инициализация членов
Ввод и вывод с использованием класса Console
Средства форматирования строк в C#
Структурные и ссылочные типы
Структурные и ссылочные типы: исследуем дальше
Точка отсчета для любых типов: System.Object
Замещение методов System.Object
Статические члены System.Object
Системные типы данных и псевдонимы C#
Избранные заметки о некоторых типах данных
От структурного типа к ссылочному типу и наоборот: упаковка и распаковка
Значения по умолчанию для встроенных типов данных
Константы
Циклы в C#
Выражение for
Выражение foreach/in
Выражения while и do/while
Средства управления логикой работы программ
в C# Дополнительные операторы C#
Определение пользовательских методов класса
Модификаторы уровня доступа к методам
Статические методы и методы экземпляров
Статические данные
Интересное рядом: некоторые статические члены класса Environment
Модификаторы для параметров методов
Работа с массивами
Многомерные массивы
Базовый класс System.Array
Работа со строками
Управляющие последовательности и вывод служебных символов
Применение System.Text.StringBuilder
Перечисления C#
Базовый класс System.Enum
Определение структур в C#
Еще раз об упаковке и распаковке
Определяем пользовательские пространства имен
Применение пространств имен для разрешения конфликтов между именами классов
Использование псевдонимов для имен классов
Вложенные пространства имен
Подведение итогов
Глава 3. C# и объектно-ориентированное программирование
Формальное определение класса в C#
Ссылки на самого себя
Определение открытого интерфейса по умолчанию
Указание области видимости на уровне типа: открытые и внутренние типы
Столпы объектно-ориентированного программирования
Инкапсуляция
Наследование: отношения "быть" и "иметь"
Полиморфизм: классический и для конкретного случая
Средства инкапсуляции в C#
Реализация инкапсуляции при помощи традиционных методов доступа и изменения
Второй способ инкапсуляции: применение свойств класса
Внутреннее представление свойств C#
Свойства только для чтения, только для записи и статические
Псевдоинкапсуляция: создание полей "только для чтения"
Статические поля "только для чтения"
Поддержка наследования в C#
Работа с конструктором базового класса
Можно ли производить наследование от нескольких базовых классов
Хранение "семейных тайн": ключевое слово protected
Запрет наследования: классы, объявленные как sealed
Применение модели включения-делегирования
Определение вложенных типов
Поддержка полиморфизма в C#
Абстрактные классы
Принудительный полиморфизм: абстрактные методы
Контроль версий членов класса
Приведение типов в C#
Приведение числовых типов
Обработка исключений
Генерация исключения
Перехват исключений
Создание пользовательских исключений, первый этап
Создание пользовательских исключений, второй этап
Обработка нескольких исключений
Блок finally
Последние замечания о работе с исключениями
Жизненный цикл объектов
Завершение ссылки на объект
Завершение в подробностях
Создание метода удаления для конкретного случая
Интерфейс IDisposable
Взаимодействие со сборщиком мусора
Оптимизация сборки мусора
Подведение итогов
Глава 4. Интерфейсы и коллекции
Программирование с использованием интерфейсов
Реализация интерфейса
Получение ссылки на интерфейс
Интерфейсы как параметры
Явная реализация интерфейса
Создание иерархий интерфейсов
Наследование от нескольких базовых интерфейсов
Создание пользовательского нумератора (интерфейсы IEnumerable и IEnumerator)
Создание клонируемых объектов (интерфейс ICloneable)
Создание сравниваемых объектов (интерфейс IComparable)
Сортировка по нескольким идентификаторам (IComparer)
Как с помощью специальных свойств сделать сортировку более удобной
Пространство имен System.Collections
Пространство имен System.Collections.Specialized
Применение ArrayList
Подведение итогов
Глава 5. Дополнительные возможности классов C#
Создание пользовательского индексатора
Перегрузка операторов
Перегрузка операторов равенства
Перегрузка операторов сравнения
Последние замечания о перегрузке операторов
Делегаты
Пример делегата
Делегаты как вложенные типы
Члены System.MulticastDelegate
Применение CarDelegate
Анализ работы делегата
Многоадресность
Делегаты, указывающие на обычные функции
События
Как работают события
Прием событий
Объекты как приемники событий
Реализация обработки событий с использованием интерфейсов
Документирование в формате XML
Просмотр файла документации в формате XML
Создание документации в формате HTML
Подведение итогов
Глава 6. Сборки, потоки и домены приложений
Проблемы с классическими двоичными файлами
COM
Проблема: работа с версиями COM
Проблема: развертывание приложений COM
Обзор сборок .NET
Сборки из одного и нескольких файлов
Логическое и физическое представление сборки
Сборки обеспечивают повторное использование кода
Сборки - контейнеры для типов
В сборках предусмотрены встроенные средства самоописания и контроля версий
Сборки определяют контекст безопасности
Разные версии сборок могут выполняться параллельно
Создание тестовой однофайловой сборки
Клиентское приложение C#
Клиентское приложение Visual Basic.NET
Межъязыковое наследование
Подробности манифеста CarLibrary
Метаданные и код IL для типов CarLibrary
Частные сборки
Технология "зондирования"
Идентификация частной сборки
Частные сборки и файлы конфигурации приложений
Процесс загрузки частной сборки в целом
Сборки для общего доступа
Проблемы с GAC?
Общие ("сильные") имена сборок
Создание сборки для общего пользования
Установка сборки в глобальный кэш сборок (GAC)
Применение сборки для общего пользования в приложении
Политика версий .NET
Запись информации о версии
Работа с разными версиями SharedAssembly
Создаем SharedAssembly версии 2.0
Политика версий .NET по умолчанию
Управление загрузкой разных версий сборок
Администраторский файл конфигурации
Работа с потоками в традиционных приложениях Win32
Проблемы одновременности и синхронизации потоков
Что такое домен приложения
Работаем с доменами приложений
Пространство имен System.Threading
Работа с классом Thread
Запуск вторичных потоков
Именованные потоки
Параллельная работа потоков
Как "усыпить" поток
Одновременный доступ к данным из разных потоков
Ключевое слово lock
Использование System.Threading.Monitor
Применение System.Threading.Interlocked
Подведение итогов
Глава 7. Рефлексия типов и программирование с использованием атрибутов
Что такое рефлексия типов
Класс Type
Получение объекта класса Type
Возможности класса Type
Типы пространства имен System.Reflection
Загрузка сборки
Вывод информации о типах в сборке
Вывод информации о членах класса
Вывод информации о параметрах метода
Применение позднего связывания
Класс System.Activator
Применение динамических сборок
Знакомство с пространством имен System.Reflection.Emit
Создаем динамическую сборку
Как использовать динамическую сборку
Программирование с использованием атрибутов
Работа с существующими атрибутами
Создание пользовательских атрибутов
Как ограничить использование атрибута определенными типами
Атрибуты уровня сборки и модуля
Файл AssemblyInfo.cs
Как работать с атрибутами в процессе выполнения программы
Подведение итогов
Глава 8. Окна становятся лучше: введение в Windows.Forms
Два главных пространства имен для организации графического интерфейса
Обзор пространства имен Windows.Forms
Взаимодействие с типами Windows.Forms
Создание нового проекта
Создание главного окна приложения (вручную)
Создание проекта Windows Forms
Класс System.Windows.Forms.Application
Работаем с классом Application
Реагируем на событие ApplicationExit
Препроцессинг сообщений при помощи класса Application
Анатомия формы
Классы System.Object и MarshalByRefObject
Класс Component
Класс Control
Настройка стиля формы
События класса Control
Работаем с классом Control
Реагируем на события мыши: часть первая
Реагируем на события мыши: часть вторая
Реагируем на события клавиатуры
Еще немного о классе Control
Painting Basics
Класс ScrollableControl
Класс ContainerControl
Класс Form
Используем возможности класса Form
Создаем меню
Вложенный класс Menu$MenuItemCollection
Создание системы меню в приложении
Добавляем еще одно меню верхнего уровня
Создаем контекстное меню
Дополнительные возможности меню
Создаем меню при помощи IDE Visual Sudio.NET
Что такое строка состояния
Создаем строку состояния
Работаем с классом Timer
Отображение в строке состояния подсказок к пунктам меню
Создаем панель инструментов
Создаем панели инструментов при помощи Visual Studio IDE
Создаем набор изображений (объект ImageList) при помощи
Visual Studio
Пример приложения Windows Forms для работы с реестром и журналом событий Windows 2000
Взаимодействие с системным реестром
Взаимодействие с журналом событий Windows 2000
Как считать информацию из журнала событий
Подведение итогов
Глава 9. Графика становится лучше (GDI+)
Обзор пространств имен GDI+
Пространство имен System.Drawing
Служебные типы System.Drawing
Тип Point(F)
Тип Rectangle(F)
Тип Size(F)
Класс Region
Сеансы вывода графики
Как сделать клиентскую область вашего приложения "недействительной"
Выводим графические объекты без события Paint
Возможности класса Graphics
Система координат по умолчанию в GDI+
Применение альтернативных единиц измерения
Применение альтернативных точек отсчета
Работа с цветом
Возможности класса ColorDialog
Работа со шрифтами
Семейства шрифтов
Единицы измерения для шрифта
Создаем приложение с возможностью выбора шрифта
Выводим информацию об установленных шрифтах (System.Drawing.Text)
Класс FontDialog
Обзор пространства имен System.Drawing.Drawing2D
Определение качества вывода графического объекта
Работа с перьями
Работаем с "наконечниками" перьев
Работаем с кистью
Работаем со штриховыми кистями
Работаем с текстурными кистями
Работаем с градиентными кистями
Вывод изображений
Перетаскивание, проверка попадания в область, занимаемую изображением,и элемент управления PictureBox
Еще о проверке попадания
Проверка попадания в непрямоугольные области
Работа с форматами реcурсов .NET
Пространство имен System.Resources
Создание файлов *.resx программным образом
Чтение из файлов *.resx программным образом
Создание файлов *.resources
Работаем с типом ResourceWriter
Работаем с типом ResourceManager
Работа с ресурсами при помощи IDE Visual Studio.NET
Подведение итогов
Глава 10. Элементы управления
Иерархия классов элементов управления
Как вручную добавить элементы управления на форму
Класс Control$ControlCollection
Как добавить элементы управления на форму при помощи Visual Studio
Элемент управления TextBox
Некоторые возможности TextBox
Великая и могучая кнопка (а также родительский класс ButtonBase)
Выравнивание текста и изображений относительно краев кнопки
Некоторые возможности работы с кнопками
Работаем с флажками
Работаем с переключателями и группирующими рамками
Элемент управления CheckedListBox
Списки
Комбинированные списки
Настраиваем порядок перехода по Tab
Tab Order Wizard
Элемент управления TrackBar
Элемент управления MonthCalendar
Еще немного о типе DateTime
Элементы управления UpDown
Элемент управления Panel
Всплывающие подсказки (ToolTips) для элементов управления
Добавление всплывающей подсказки при помощи графических средств Visual Studio
Элемент управления ErrorProvider
Закрепление элемента управления в определенном месте формы
Стыковка элемента управления с краем формы
Создание пользовательских диалоговых окон
Пример использования диалогового окна в приложении
Как получить данные из диалогового окна
Наследование форм
Подведение итогов
Глава 11. Ввод, вывод и сериализация объектов
Знакомство с пространством имен System.IO
Типы Directory(Info) и File(Info)
Абстрактный класс FileSystemInfo
Работа с типом DirectoryInfo
Перечисление FileAttributes
Получение доступа к файлам через объект DirectoryInfo
Создаем подкаталоги при помощи класса DirectoryInfo
Статические члены класса Directory
Класс FileInfo
Использование метода FileInfo.Open()
Методы FileInfo.OpenRead() и FileInfo.OpenWrite()
Методы FileInfo.OpenText(), FileInfo.CreateText() и FileInfo.AppendText()
Абстрактный класс Stream
Работа с объектом FileStream
Класс MemoryStream
Класс BufferedStream
Классы StreamWriter и StreamReader
Запись в текстовый файл
Считывание информации из текстового файла
Класс StringWriter
Класс StringReader
Работа с двоичными данными (классы BinaryReader и BinaryWriter)
Заметки на полях
Сохранение объектов в .NET
Графы для отношений объектов
Настройка объектов для сериализации
Выбираем объект Formatter
Пространство имен System.Runtime.Serialization
Сериализация в двоичном формате
Сериализация в формате SOAP
Сериализация в пользовательском формате и интерфейс ISerializable
Простой пример пользовательской сериализации
Приложение для регистрации автомобилей с графическим интерфейсом
Реализация добавления новых объектов Car
Код сериализации
Подведение итогов
Глава 12. Взаимодействие с унаследованным программным кодом
Главные вопросы совместимости
Пространство имен System.Runtime.InteropServices
Взаимодействие с модулями DLL, созданными на C
Поле ExactSpelling
Поле CharSet
Поля CallingConvention и EntryPoint
Взаимодействие .NET и COM
Представление типов COM как типов .NET
Управление ссылками на объекты сокласса
Сокрытие низкоуровневых интерфейсов COM
Создание простого COM-сервера в Visual Basic 6.0
Что находится в IDL нашего COM-сервера
Создаем простой клиент COM в Visual Basic 6.0
Импорт библиотеки типов
Добавление ссылки на сборку
Раннее связывание с COM-классом CoCalc
Позднее связывание с соклассом CoCalc
Особенности созданной нами сборки
Создаем COM-сервер при помощи ATL
Добавление методов в интерфейс по умолчанию
Генерация события COM
Генерация ошибки COM
Представление внутренних подобъектов и применение SAFEARRAY
Последний штрих: создаем перечисление IDL
Клиент COM-сервера в Visual Basic 6.0
Создаем сборку и анализируем процесс преобразования
Преобразование библиотеки типов
Преобразование интерфейсов COM
Преобразование атрибутов параметров в коде IDL
Преобразование иерархии интерфейсов
Преобразование соклассов и свойств COM
Преобразование перечислений COM
Преобразование COM SAFEARRAY
Перехват событий COM
Обработка ошибки COM
Полный код клиента C#
Обращение клиента COM к сборке .NET
Роль CCW
Понятие интерфейса класса
Определяем интерфейс класса
Создание типа .NET
Генерация библиотеки типов и регистрация типов .NET
Анализ созданной библиотеки типов
Интерфейс _Object
Сгенерированное выражение библиотеки
Просмотр типов .NET в OLE/COM Object Viewer
Просмотр записей в реестре
Создаем клиента в Visual Basic 6.0
Некоторые особенности отображения типов .NET в COM
Анализ кода COM, сгенерированного для базового класса
Анализ кода COM, сгенерированного для производного класса
Управление процессом генерации кода IDL (как повлиять на то, что делает утилита tlbexp.exe)
Что у нас получилось
Управление регистрацией промежуточного модуля CCW
Взаимодействие со службами COM+
Пространство имен System.EnterpriseServices
Особенности создания типов .NET для работы
под COM+ Пример класса C# для работы под COM+
Добавление атрибутов уровня сборки для COM+
Помещаем сборку в каталог COM+
Проверяем установку при помощи Component Services Explorer
Подведение итогов
Глава 13. Доступ к данным при помощи ADO.NET
Почему потребовалось создавать ADO.NET
ADO.NET: общая картина
Знакомство с пространствами имен ADO.NET
Типы пространства имен System.Data
Тип DataColumn
Создаем объект DataColumn
Добавляем объект DataColumn в DataTable
Делаем столбец первичным ключом таблицы
Настройка автоматического увеличения значений для столбцов
Настраиваем представление столбца в формате XML
Тип DataRow
Работа со свойством DataRow.RowState
Работа со свойством ItemArray
Тип DataTable
Создаем объект DataTable
Удаляем строки из таблицы
Применение фильтров и порядка сортировки для DataTable
Внесение изменений в строки
Тип DataView
Возможности класса DataSet
Члены класса DataSet
Создание объекта DataSet
Моделируем отношения между таблицами при помощи класса DataRelation
Переход между таблицами, участвующими в отношении
Чтение и запись объектов DataSet в формате XML
Создание примера простой базы данных
Управляемые провайдеры ADO.NET
Управляемый провайдер OLE DB
Установление соединения при помощи типа OleDbConnection
Построение команды SQL
Работа с OleDbDataReader
Подключение к базе данных Access
Выполнение хранимых процедур
Тип OleDbDataAdapter
Заполнение данными объекта DataSet при помощи OleDbDataAdapter
Работа с управляемым провайдером SQL
Пространство имен System.Data.SqlTypes
Вставка новых записей при помощи SqlDataAdapter
Изменение записей в таблице при помощи SqlDataAdapter
Автоматическое создание команд SQL
Заполнение объекта DataSet с несколькими таблицами и добавление объектов DataRelations
Подведение итогов
Глава 14. Разработка web-приложений и ASP.NET
Web-приложения и web-серверы
Что такое виртуальные каталоги
Структура документа HTML
Форматирование текста средствами HTML
Заголовки HTML
HTML-редактор Visual Studio.NET
Разработка форм HTML
Создаем пользовательский интерфейс
Добавление изображений
Клиентские скрипты
Пример клиентского скрипта
Реализация проверки введенных пользователем данных
Передаем данные формы (методы GET и POST)
Синтаксис строки запроса HTTP
Создание классической страницы ASP
Принимаем данные, переданные методом POST
Первое приложение ASP.NET
Некоторые проблемы классических ASP
Некоторые преимущества ASP.NET
Пространства имен ASP.NET
Наиболее важные типы пространства имен System.Web
Приложение и сеанс подключения пользователя
Создание простого web-приложения на C#
Исходный файл *.aspx
Файл web.config
Исходный файл Global.asax
Простой код ASP.NET на C#
Архитектура web-приложения ASP.NET
Тип System.Web.UI.Page
Связка *.aspx/Codebehind
Свойство Page.Request
Свойство Page.Response
Свойство Page.Application
Отладка и трассировка приложений ASP.NET
Элементы управления WebForm
Создание элементов управления WebForm
Иерархия классов элементов управления WebForm
Виды элементов управления WebForm
Базовые элементы управления WebForm
Элементы управления с дополнительными возможностями
Элементы управления для работы с источниками данных
Элементы управления для проверки вводимых пользователем данных
Обработка событий элементов управления WebForm
Подведение итогов
Глава 15. Web-службы
Роль web-служб
Инфраструктура web-службы
Протокол подключения
Служба описания
Служба обнаружения
Обзор пространств имен web-служб
Пространство имен System.Web.Services
Пример элементарной web-службы
Исходный файл C# для web-службы (*.asmx.cs)
Реализуем методы web-службы
Работа клиента с web-службой
Тип WebMethodAttribute
Базовый класс System.Web.Services.WebService
Web Service Description Language (WSDL)
Протоколы подключения к web-службам
Обмен данными при помощи HTTP-GET и HTTP-POST
Обмен данными при помощи SOAP
Прокси-сборки для web-служб
Создание прокси-сборки при помощи утилиты wsdl.exe
Компилируем прокси-сборку
Создание клиента для работы через прокси-сборку
Создание прокси-сборки в Visual Studio.NET
Пример более сложной web-службы (и ее клиентов)
Сериализация пользовательских типов
Настраиваем клиента web-службы
Создание типов для сериализации (некоторые уточнения)
Протокол обнаружения web-службы
Добавление новой web-службы
Подведение итогов
Алфавитный указатель
ОТРЫВОК
Философия .NET
Любому современному программисту, который желает идти в ногу с последними веяниями, каждые несколько лет приходится переучиваться. Языки (C++, Visual Basic, Java), библиотеки (MFC, ATL, STL), архитектуры (COM, CORBA), которые стали вехами в развитии программирования за последние годы, постепенно уходят в тень лучших или по крайней мере более молодых программных технологий. Вне зависимости от того, нравится это программистам или нет, этот процесс неизбежен. Платформа .NET компании Microsoft - это следующая волна коренных изменений, которая идет к нам из Редмонда.
В этой главе мы рассмотрим основополагающие понятия .NET, к которым мы затем будем обращаться во всех остальных частях книги. Глава начинается с рассмотрения элементов, на которых основана платформа .NET - сборок (assemblies), промежуточного языка (intermediate language, IL) и компиляции в процессе выполнения (just in time compilation, JIT). Мы также рассмотрим взаимосвязи компонентов платформы .NET - Common Language Runtime (CLR), Common Type System (CTS) и Common Language Specification (CLS).
В этой главе также приведен общий обзор возможностей, которые предоставляют библиотеки базовых классов .NET и утилит (таких как ILDasm.exe), которые помогут вам в работе с этими библиотеками. В самом конце главы мы познакомимся с возможностями компиляции приложений C# с использованием компилятора командной строки (csc.exe) и интегрированной среды разработки (Integrated Development Environment, IDE) Visual Studio.NET.
Современное состояние дел
Перед тем как мы вступим в пределы вселенной .NET, полезно будет разобраться с простым вопросом - а зачем, собственно говоря, она нужна? Для этого мы очень кратко рассмотрим те технологии, которые имеются в распоряжении программистов в настоящий момент, их возможности и ограничения, а затем перейдем к тем преимуществам, которые предоставляют C# и платформа .NET в целом.
Как живут программисты, использующие Win32/C
Изначально под программированием под Windows подразумевалось программирование на C с использованием Windows Application Programming Interface (интерфейсом прикладного программирования Windows, в 32-разрядных версиях Windows - Win32 API). С использованием этой технологии было создано множество вполне достойных приложений, однако вряд ли кто-нибудь будет спорить с тем, что написание приложения с использованием только Windows API - это очень трудоемкая задача.
Еще одна проблема заключается в том, что C - достаточно суровый по отношению к программисту язык. Тем, кто создает на нем свои приложения, приходится вручную заниматься управлением памятью, выполнять расчеты при использовании указателей и работать с совершенно неестественными с точки зрения человеческого языка синтаксическими конструкциями. Кроме того, в C, конечно, недостаточно возможностей для объектно-ориентированного программирования. Если вам приходилось связывать тысячи глобальных функций Win32 API в единые гигантские конструкции, вы не будете сил
ьно удивляться тому, что в подобных приложениях ошибки встречаются очень часто.
Как живут программисты, использующие C++/MFC
C++ - это огромный шаг вперед в отношении новых возможностей по сравнению с исходным языком С. Во многих ситуациях C++ вполне допустимо представить как объектно-ориентированную надстройку над C. Такая надстройка позволяет использовать преимущества "столпов" объектно-ориентированного программирования - инкапсуляции, полиморфизма и наследования. Однако программисты, использующие C++, остаются незащищенными от многих и часто опасных особенностей C (теми же самыми низкоуровневыми возможностями работы с памятью и трудными для восприятия синтаксическими конс
трукциями).
Существует множество библиотек для C++, основное назначение которых - облегчить написание приложений под Windows, предоставив для этой цели уже готовые классы. Одна из наиболее распространенных библиотек - это MFC (Microsoft Foundation Classes). MFC - это дополнительный уровень над Win32 API, который значительно упрощает работу программиста за счет использования готовых классов, макросов и мастеров. Однако MFC - это лишь частичное решение проблемы. Даже при использовании MFC программисту приходится работать со сложным для чтения кодом, весьма опасным с точки зрения возможных ошибо
к.
Как живут программисты, использующие Visual Basic
Люди всегда стремятся сделать свою жизнь проще. Повинуясь этому стремлению многие программисты на C++ обратили свои взоры к гораздо более простому и дружелюбному языку, каким является Visual Basic (VB). Visual Basic позволяет работать с достаточно сложными элементами интерфейса пользователя, библиотеками кода (например, COM-серверами) и средствами доступа к данным при минимальных затратах времени и сил. Visual Basic в гораздо большей степени, чем MFC, прячет от пользователя вызовы Win32 API и предоставляет большой набор интегрированных средств быстрой разработки.
Однако у Visual Basic есть и недостатки. Главный из них - это гораздо меньшие возможности, которые предоставляет этот язык, по сравнению с С++ (это утверждение справедливо, по крайней мере, для версий более ранних, чем VB.NET). Visual Basic - это язык "для работы с объектами", а не объектно-ориентированный язык в обычном понимании этого слова. В Visual Basic нет классического наследования, нет поддержки создания параметризованных классов, нет собственных средств создания многопоточных приложений - и этот список можно продолжать еще долго.
Как живут программисты, использующие Java
Язык программирования Java - это полностью объектно-ориентированный язык, который в отношении синтаксиса многое унаследовал от C++. Конечно, преимущества Java далеко не исчерпываются межплатформенностью. Язык Java в синтаксическом отношении проще и логичнее, чем C++. Java как платформа предоставляет в распоряжение программистов большое количество библиотек (пакетов), в которых содержится большое количество описаний классов и интерфейсов на все случаи жизни. С их помощью можно создавать стопроцентные приложения Java с возможностью обращения к базам данных, подде
ржкой передачи почтовых сообщений, с клиентской частью, которой необходим только web-браузер, или наоборот, с клиентской частью, обладающей изощренным интерфейсом.
Java - это очень элегантный и красивый язык. Однако при его использовании проблем также избежать не удастся. Одна из серьезных проблем заключается в том, что при создании сложного приложения на Java вам придется использовать только этот язык для создания всех частей этого приложения. В Java предусмотрено не так уж много средств для межъязыкового взаимодействия (что понятно ввиду предназначения Java быть единым многоцелевым языком программирования). В реальном мире существуют миллионы строк готового кода, которые хотелось бы интегрировать с новыми приложениям
и на Java. Однако это сделать очень трудно.
Java - это далеко не идеальный язык во многих ситуациях. Простой пример - если вы попытаетесь создать только на Java приложение, активно работающее с 3D-графикой, скорее всего, вы обнаружите, что работать такое приложение будет не очень быстро. Немного подумав, вы можете прийти к выводу, что для работы с 3D-графикой лучше использовать код, написанный на языке с более развитыми низкоуровневыми возможностями (например, на C++). Однако интегрировать такой код с кодом на Java вам будет очень сложно. Поскольку возможности для обращения к API компонентов, созданных на других
языках, в Java очень ограничены, говорить о реальном межъязыковом взаимодействии на основе Java не приходится.
Как живут COM-программисты
Современное состояние дел таково, что если вы не строите Java-приложения, то велика вероятность, что вы осваиваете технологию Microsoft Component Object Model (COM). COM-технология провозглашает: "Если вы создаете классы в точном соответствии с требованиями COM, то у вас получится блок повторно используемого программного кода".
Прелесть двоичного COM-сервера заключается в том, что к нему можно обращаться из любого языка. Например, программисты, использующие C++, могут создавать классы, которые можно будет использовать из приложения на VBasic. Программисты, использующие Delphi, могут использовать классы, созданные на C и т. д. Однако в межъязыковом взаимодействии COM есть свои ограничения. Например, вы не можете произвести новый тип COM от существующего (то есть не можете использовать классическое наследование). Для повторного использования существующих типов COM вам придется использовать др
угие, гораздо менее надежные и эффективные средства.
Большое преимущество COM заключается в том, что программист может не заботиться о физическом местонахождении компонентов. Такие средства, как Application Identifiers (AppIDs, идентификаторы приложений), стабы (stubs), прокси, среда выполнения COM, позволяют избегать при обращении к компонентам по сети необходимости помещать в приложение код для работы с сокетами, вызовами RPC и прочими низкоуровневыми механизмами. Достаточно посмотреть на такой код на Visual Basic 6.0 для клиента COM:
'Этот код на VB 6.0 предназначен для активации класса COM,
'созданного на любом языке. Класс COM может быть расположен
'в любом месте на локальном компьютере или в сети.
Dim c as New MyCOMClass 'Местонахождение класса 'определяется через AppID c.DoSomeWork
Объектная модель COM используется очень широко. Однако внутреннее устройство компонентов весьма сложно. Чтобы научиться разбираться в нем, вам придется потратить по крайней мере несколько месяцев. Написание приложений с использованием COM-компонентов можно упростить, используя стандартные библиотеки, например библитеку Active Template Library (ATL) со своим набором готовых классов, шаблонов и макросов.
Некоторые языки (например, Visual Basic) также позволяют скрыть сложность инфраструктуры COM. Однако всех сложностей избежать все равно не удастся. Например, даже если вы работаете с относительно простым и поддерживающим COM Visual Basic, вам придется решать не всегда простые вопросы, связанные с регистрацией компонентов на компьютере и развертыванием приложений.
Как живут программисты, использующие Windows DNA
Картина будет неполной, если мы не примем во внимание такую мелочь, как Интернет. За несколько последних лет Microsoft добавила в свои операционные системы большое количество средств для работы с этой средой, в том числе и средства, призванные помочь в создании Интернет-приложений. Однако построение законченного web-приложения с использованием технологии Windows DNA (Distributed iNternet Architecture - распределенная межсетевая архитектура) до сих пор остается весьма сложной задачей.
Значительная часть сложностей возникает оттого, что Windows DNA требует использования разнородных технологий и языков (ASP, HTML, XML, JavaScript, VBScript, COM(+), ADO и т. д.). Одна из проблем заключается в том, что синтаксически все эти языки и технологии очень мало похожи друг на друга. Например, синтаксис JavaScript больше похож на синтаксис C, в то время как VBScript является подмножеством Visual Basic. COM-серверы, предназначенные для работы в среде выполнения COM+, созданы на основе совершенно иных подходов, нежели ASP-страницы, которые к ним обращаются. Конечным результатом является пугающее см
ешение технологий. Помимо всего прочего, в каждом языке, который входит в состав Windows DNA, предусмотрена своя система типов, что также не является источником большой радости для программистов. Например, тип данных int в JavaScript - это не то же самое, что int в C, который, в свою очередь, отличен от integer в Visual Basic.
Решение .NET
На этом мы будем считать обращение к новейшей истории программирования законченным. Главный вывод, с которым вряд ли кто-нибудь будет спорить, таков: тяжела жизнь Windows-программиста. На этом фоне возможности, предлагаемые платформой .NET, позволяют радикально облегчить нашу жизнь. Один из главных принципов .NET звучит так: "Изменяйте все, что хотите, откуда вам угодно". .NET - это совершенно новая модель для создания приложений под Windows (а в будущем, видимо, и под другими операционными системами). Вот краткое перечисление основных возможностей .NET:
Полные возможности взаимодействия с существующим кодом. Вряд ли кто-нибудь будет спорить, что это - вещь очень хорошая. Как мы увидим в главе 12, существующие двоичные компоненты COM отлично работают вместе с двоичными файлами .NET.
Полное и абсолютное межъязыковое взаимодействие. В отличие от классического COM, в .NET поддерживаются межъязыковое наследование, межъязыковая обработка исключений и межъязыковая отладка.
Общая среда выполнения для любых приложений .NET, вне зависимости от того, на каких языках они были созданы. Один из важных моментов при этом - то, что для всех языков используется один и тот же набор встроенных типов данных.
Библиотека базовых классов, которая обеспечивает сокрытие всех сложностей, связанных с непосредственным использованием вызовов API, и предлагает целостную объектную модель для всех языков программирования, поддерживающих .NET.
Про пугающую сложность COM можно забыть! IClassFactory, IUnknown, код IDL и проклятые VARIANT-совместимые типы данных (BSTR, SAFEARRAY и остальные) больше не встретятся вам в коде программ .NET.
Действительное упрощение процесса развертывания приложения. В .NET нет необходимости регистрировать двойные типы в системном реестре. Более того, .NET позволяет разным версиям одного и того же модуля DLL мирно сосуществовать на одном компьютере.
Строительные блоки .NET (CLR, CTS и CLS)
Технологии CLR, CTS и CLS очень важны для понимания смысла платформы .NET. С точки зрения программиста .NET вполне можно рассматривать просто как новую среду выполнения и новую библиотеку базовых классов. Среда выполнения .NET как раз и обеспечивается с помощью Common Language Runtime (CLR, стандартная среда выполнения для языков). Главная роль CLR заключается в том, чтобы обнаруживать и загружать типы .NET и производить управление ими в соответствии с вашими командами. CLR берет на себя всю низкоуровневую работу - например, автоматическое управление памятью, межъязыковым взаимоде
йствием, развертывание (с отслеживанием версий) различных двоичных библиотек.
Еще один строительный блок платформы .NET - это Common Type System (CTS, стандартная система типов). CTS полностью описывает все типы данных, поддерживаемые средой выполнения, определяет, как одни типы данных могут взаимодействовать с другими и как они будут представлены в формате метаданных .NET (подробнее о метаданных будет рассказано ниже в этой главе).
Важно понимать, что не во всех языках программирования .NET обязательно должны поддерживаться все типы данных, которые определены в CTS. Common Language Specification (CLS) - это набор правил, определяющих подмножество общих типов данных, в отношении которых гарантируется, что они безопасны при использовании во всех языках .NET. Если вы создаете типы .NET с использованием только тех возможностей, которые совместимы с CLS, тем самым вы сделаете их пригодными для любых языков .NET.
Библиотека базовых классов .NET
Помимо спецификаций CLR и CTS/CLS платформа .NET предоставляет в ваше распоряжение также библиотеку базовых классов, доступную из любого языка программирования .NET. Библиотека базовых классов не только прячет обычные низкоуровневые операции, такие как файловый ввод-вывод, обработка графики и взаимодействие с оборудованием компьютера, но и обеспечивает поддержку большого количества служб, используемых в современных приложениях.
В качестве примера можно привести встроенные типы для обращения к базам данных, работы с XML, обеспечения безопасности при работе приложения, создания приложений для работы в Web (конечно, обеспечивается и поддержка обычных консольных и оконных приложений). С концептуальной точки зрения отношения между уровнем среды выполнения и библиотекой базовых классов .NET выглядят так, как показано на рис. 1.1.
Преимущества C#
Специально для платформы .NET Microsoft был разработан новый язык программирования C#. C# - это язык программирования, синтаксис которого очень похож на синтаксис Java (но не идентичен ему). Например, в C# (как в Java) определение класса состоит из одного файла (*.cs), в отличие от C++, где определение класса разбито на заголовок (*.h) и реализацию (*.cpp). Однако называть C# клоном Java было бы неверно. Как C#, так и Java основаны на синтаксических конструкциях C++. Если Java во многих отношениях можно назвать очищенной версией C++, то C# можно охарактеризовать как очищенную версию Java.
Синтаксические конструкции C# унаследованы не только от C++, но и от Visual Basic. Например, в C#, как и в Visual Basic, используются свойства классов. Как C++, C# позволяет производить перегрузку операторов для созданных вами типов (Java не поддерживает ни ту, ни другую возможность). C# - это фактически гибрид разных языков. При этом C# синтаксически не менее (если не более) чист, чем Java, так же прост, как Visual Basic, и обладает практически той же мощью и гибкостью, что и C++. Подводя итоги, еще раз выделим основные особенности C#.
Указатели больше не нужны! В программах на C#, как правило, нет необходимости в работе с ними (однако если вам это потребуется, пожалуйста, - возможности для работы с указателями в вашем распоряжении).
Управление памятью производится автоматически.
В C# предусмотрены встроенные синтаксические конструкции для работы с перечислениями, структурами и свойствами классов.
В C# осталась возможность перегружать операторы, унаследованные от C++. При этом значительная часть возникавших при этом сложностей ликвидирована.
Предусмотрена полная поддержка использования программных интерфейсов. Однако в отличие от классического COM применение интерфейсов - это не единственный способ работы с типами, используя различные двоичные модули. .NET позволяет передавать объекты (как ссылки или как значения) через границы программных модулей.
Также предусмотрена полная поддержка аспектно-ориентированных программных технологий (таких как атрибуты). Это позволяет присваивать типам характеристики (что во многом напоминает COM IDL) для описания в будущем поведения данной сущности.
Возможно, самое важное, что необходимо сказать про язык C#, - это то, что он генерирует код, предназначенный для выполнения только в среде выполнения .NET. Например, вы не сможете использовать C# для создания классического COM-сервера. Согласно терминологии Microsoft код, предназначенный для работы в среде выполнения .NET, - это управляемый код (managed code). Двоичный файл, который содержит управляемый файл, называется сборкой (assembly). Подробнее об этом будет сказано ниже.
Языки программирования .NET
Во время анонса платформы .NET на конференции 2000 Professional Developers Conference (PDC) докладчики называли фирмы, работающие над созданием .NET-версий своих компиляторов. На момент написания этой книги компиляторы для создания .NET-версий приложений разрабатывались более чем для 30 различных языков. Помимо четырех языков, поставляемых с Visual Studio.NET (C#, Visual Basic.NET, "Managed C++" и JScript.NET), ожидаются .NET-версии Smalltalk, COBOL, Pascal, Python, Perl и множества остальных известных языков программирования. Общая картина представлена на рис. 1.2.
Может показаться смешным, но двоичные файлы .NET, для которых используются стандартные расширения DLL и EXE, по своему внутреннему содержанию не имеют абсолютно ничего общего с обычными исполняемыми файлами. Например, файлы DLL не предоставляют свои методы в распоряжение приложений на компьютере. В отличие от компонентов COM двоичные файлы .NET не описываются с помощью кода IDL и регистрируются в системном реестре. Однако, пожалуй, самое важное отличие заключается в том, что двоичные файлы .NET не содержат зависящих от платформы команд. Содержимое двоичных файлов .N
ET - это платформенно-независимый "промежуточный язык", который официально называется Microsoft Intermediate Language (MSIL, промежуточный язык Microsoft), или просто IL.
Обзор двоичных файлов .NET ("сборки")
Когда с помощью компилятора для платформы .NET создается модуль DLL или EXE, содержимое этого модуля - это так называемая сборка (assembly) на языке IL. Мы будем рассматривать сборки более подробно в главе 6. Однако для того, чтобы лучше понять особенности среды выполнения .NET, нам потребуется охарактеризовать хотя бы некоторые базовые свойства этого нового формата исполняемых файлов.
Как уже говорилось, сборка содержит код на "промежуточном языке" - IL. Назначение IL концептуально аналогично байт-коду Java - он компилируется в платформенно-специфичные инструкции, только когда это абсолютно необходимо. "Абсолютная необходимость" возникает тогда, когда к блоку инструкций IL (например, реализации метода) обращается для использования среда выполнения .NET.
Помимо инструкций IL, двоичные модули .NET содержат также метаданные, которые подробно описывают все типы, использованные в модуле. Например, если у вас внутри сборки есть класс Foo, то в метаданных этой сборки будет содержаться информация о базовом классе для Foo, какие интерфейсы предусмотрены для Foo (если они вообще предусмотрены), а также полное описание всех методов, свойств и событий этого класса.
Во многих отношениях метаданные .NET являются значительным усовершенствованием по сравнению с классической информацией о типах в COM. Классические двоичные файлы COM обычно описываются с помощью ассоциированной библиотеки типов (которая очень похожа на двоичную версию кода IDL). Проблема с информацией о типах в COM заключается в том, что никто не может гарантировать вам, что эта информация окажется полной. Код IDL не может создать полный каталог внешних серверов, которые могут быть необходимы для нормальной работы содержащихся в модуле COM классов. Существовани
е метаданных .NET, напротив, обеспечивается тем, что метаданные автоматически генерируются компилятором, создающим приложение .NET.
Метаданные описывают не только типы, используемые в сборке, но и саму сборку. Эта часть метаданных называется манифестом (manifest). В манифесте содержится информация о текущей версии сборки, об использованных ограничениях по безопасности, о поддерживаемом естественном языке (английском, русском и т. д.), а также список всех внешних сборок, которые потребуются для нормального выполнения. Нам предстоит рассмотреть в этой главе различные средства, которые можно использовать для анализа кода IL внутри сборки, метаданных для типов и манифеста.
Сборки из одного и нескольких файлов
В подавляющем большинстве случаев двоичный файл .NET и сборка - это одно и то же и между ними существует отношение "один-к-одному". Если мы будем говорить о создании .NET DLL, то понятия "двоичный файл" и "сборка" мы будем использовать как синонимы. Однако (подробнее об этом - в главе 6) такой подход верен не всегда. Сборка может состоять как из одного, так и из нескольких двоичных файлов. В сборке из одного файла (single file assembly) этот единственный файл содержит и манифест, и метаданные, и инструкции IL.
В сборке из нескольких двоичных файлов (multifile assembly) каждый двоичный файл называется модулем (module). При создании таких многофайловых сборок один из двоичных файлов должен содержать манифест сборки (в нем могут также находиться и другие данные, в том числе инструкции IL). Все остальные модули могут содержать только метаданные типов и инструкции IL.
Зачем может потребоваться создание многофайловой сборки? Единственная причина для этого - большая гибкость при развертывании приложения. Например, если пользователь обращается к удаленной сборке, которая должна быть загружена на его локальный компьютер, среда выполнения загрузит лишь те модули сборки, которые действительно необходимы. Такое решение позволит избежать ненужного сетевого трафика и увеличить скорость работы программы.
Роль Microsoft Intermediate Language
Теперь, когда вы уже имеете представление о сборках .NET, мы можем рассмотреть Microsoft Intermediate Language (IL) - промежуточный язык Microsoft, более подробно. Код IL не зависит от платформы, на которой будет производиться выполнение. При этом компилятор для платформы .NET сгенерирует код IL вне зависимости от того, какой язык программирования (C#, Visual Basic.NET, Eiffel и т. п.) вы использовали для создания программы. Пожалуй, есть смысл продемонстрировать это более наглядно. В качестве примера мы создадим не самый сложный калькулятор. Единственное, что он у нас будет уметь делать - произво
дить сложение 10 и 84. Ниже приведен код этого калькулятора на С#. Пока можно не задумываться об особенностях синтаксиса в этом примере, но отметьте для себя код, относящийся к методу Add().
// С пространствами имен мы познакомимся чуть ниже в этой главе
namespace Calculator
{
using System;
// В классе Calculator определен метод Add(), а также точка входа
// приложения - метод Main()
public class Calc
{
// Конструктор по умолчанию
public Calc(){}
public int Add(int x, int y)
{
return x + y;
}
public static int Main(string[] args)
{
// Создаем объект Calc и складываем два числа
Calc c = new Calc();
int ans = c.Add(10, 84);
Console.WriteLine("10 + 84 is {0}.", ans);
return 0;
}
}
}
После того как этот исходный файл будет обработан компилятором C# (csc.exe), в нашем распоряжении окажется исполняемый файл C# - сборка из одного файла. Внутри этого файла можно будет обнаружить манифест, инструкции IL и метаданные, описывающие класс Calc. Если мы заглянем внутрь этой сборки (с помощью чего - об этом мы скажем ближе к концу этой главы), то помимо всего прочего мы сможем найти следующий блок инструкций IL, относящихся к методу Add():
.method public hidebysig instance int32 Add(int32 x, int32 y) il managed
{
// Размер кода 8 (0x8)
.maxstack 2
.locals ([0] int32 V_0)
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: add
IL_0003: stloc.0
IL_0004: br.s IL_0006
IL_0006: ldloc.0
IL_0007: ret
} // Конец кода IL для метода Calc:Add()
Если большая часть строк в коде IL осталась для вас загадкой, не волнуйтесь. Код IL будет рассмотрен более подробно в главе 7. Пока самое важное - отметить, что компилятор C# генерирует не платформенно-зависимый набор инструкций, а код IL. То же самое справедливо и для других компиляторов .NET. Давайте создадим наш калькулятор на языке Visual Basic.NET:
' Калькулятор VB.NET
Module Module1
' Опять-таки, в классе Calc определен метод Add()
' и точка входа для приложения
Class Calc
Public Function Add(ByVal x As Integer, ByVal y As Integer) As Integer
' Да! Теперь Visual Basic поддерживает ключевое слово 'return'
Return x + y
End function
End Class
Sub Main()
Dim ans As Integer
Dim c As New Calc()
ans = c.Add(10, 84)
Console.WriteLine("10 + 84 is {0}.", ans)
End Sub
End Module
Если мы поищем внутри сборки код, относящийся к методу Add(), то мы сможем обнаружить следующее:
.method public instance int32 Add(int32 x, int32 y) il managed
{
// Размер кода 11 (0xb)
.maxstack 2
.locals init ([0] int32 Add)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: add.ovf
IL_0004: stloc.0
IL_0005: nop
IL_0006: br.s IL_0008
IL_0008: nop
IL_0009: ldloc.0
IL_000a: ret
} // Конец метода Module1$Calc::Add
Как мы видим, получившийся код IL практически идентичен. Незначительные отличия возникают вследствие особенностей компиляторов C# и Visual Basic.NET.
Код приложений CSharpCalculator и VBCalculator можно найти в подкаталоге Chapter 1.
Преимущества IL
Возможно, к этому времени у вас уже созрел вопрос - а в чем, собственно, вообще могут состоять преимущества IL перед обычным набором платформенно-зависимых инструкций? Одно из преимуществ, про которое мы уже говорили, - возможность полного межъязыкового взаимодействия. Поскольку любой код на любом языке программирования .NET компилируется в стандартный набор инструкций IL, проблем во взаимодействии между блоками кода IL не будет. При этом взаимодействие будет производиться, как и положено, на двоичном уровне.
Еще одно возможное преимущество - потенциальная независимость от компьютерной платформы. Существует большая вероятность, что среда выполнения .NET будет распространена на самые разные компьютерные платформы и операционные системы (отличные от Windows). В результате .NET может пойти по стопам Java - то есть с помощью языков .NET можно будет создавать программы, которые будут работать под самыми разными операционными системами (и при этом в отличие от Java еще и пользоваться преимуществами языковой независимости!) Таким образом, .NET потенциально позволяет создавать п
риложения на любом языке, которые будут работать на любой платформе и под любой операционной системой.
Однако в отношении межплатформенности пока ключевое слово - "потенциально". На момент создания этой книги Microsoft официально не произнесла ни слова относительно возможности портирования среды выполнения .NET под другие операционные системы. Поэтому пока мы будем считать, что приложения .NET работают только под Windows.
Роль метаданных
Программистам, работающим с COM, хорошо знакома концепция Interface Definition Language (IDL, языка определения интерфейсов). IDL - это "метаязык", который позволяет, исключив любую двусмысленность, описать типы, используемые внутри сервера COM. IDL компилируется в двоичный формат (называемый библиотекой типов) с использованием компилятора midl.exe. Этот компилятор может использоваться любым языком, предназначенным для работы с COM.
IDL полностью описывает все типы данных, используемые в двоичном файле COM, но информация о самом этом двоичном файле в нем минимальна. Фактически она ограничивается номером версии (к примеру, 1.0, 2.0 или 2.4) и информацией о локализации (например, English, German, Russian). Кроме того, наличие или отсутствие метаданных (и их полноту) должен вручную контролировать создающий сервер COM программист - таким образом, необходимых метаданных в двоичном файле COM может вообще не оказаться.
В отличие от COM при использовании платформы .NET вам вообще не придется думать об IDL. Однако общий принцип описания типов в строго определенном двоичном формате остался.
Сборки .NET всегда содержат полные и точные метаданные, поскольку метаданные в них генерируются автоматически. Как и в IDL, в метаданных .NET содержится исчерпывающая информация об абсолютно всех типах, которые используются в сборке (классах, структурах, перечислениях и прочем), а также о каждом свойстве, методе или событии каждого типа.
Еще одно отличие метаданных .NET от информации IDL заключается в том, что метаданные .NET гораздо более подробны. В них перечислены все ссылки на внешние модули и сборки, которые потребуются для нормального выполнения сборки .NET. За счет этого можно считать сборки .NET фактически самодокументируемыми. В результате, к примеру, отпадает необходимость регистрировать двоичные файлы .NET в системном реестре (подробнее об этом будет сказано ниже).
Простой пример метаданных
Вот пример метаданных для метода Add() нашего приложения CSharpCalculator (метаданные для этого метода в VBCalculator будут точно такими же):
Method #2
___________________________
MethodName : Add (06000002)
Flags : [Public] [HideBySig] [ReuseSlot] (00000086)
RVA : 0x00002058
ImplFlags : [IL] [Managed] (00000000)
CalcCnvntn : [DEFAULT]
hasThis
ReturnType : I4
2 Arguments
Argument #1: I4
Argument #2: I4
2 Parameters
(1) ParamToken : (08000001) Name : x flags: [none] (00000000) default:
(2) ParamToken : (08000002) Name : y flags: [none] (00000000) default:
В коде ясно представлены название метода, тип возвращаемого значения и данные об ожидаемых аргументах. Как мы помним, вручную никаких метаданных мы не писали - за нас все сделал компилятор C#.
Кто будет обращаться к метаданным? И сама среда выполнения .NET (очень часто), и самые разные средства разработки и отладки. Например, средство IntelliSense в Visual Studio.Net (которое пытается помочь вам закончить начатую строку) берет необходимую ему информацию именно из метаданных. Метаданные активно используются утилитами просмотра, отладки и, конечно, самим компилятором C#.
Компиляция IL в платформенно-зависимые инструкции
Поскольку в сборках, как мы выяснили, содержится платформенно-независимый код IL, а выполняются в конечном итоге именно платформенно-зависимые инструкции, кто-то должен взять на себя работу по компиляции IL в такие инструкции. Этот "кто-то" называется "just-in-time compiler" (JIT) - компилятор времени выполнения. JIT часто ласково называют "jitter" (дрожание, трепет). JIT для перевода IL в платформенно-зависимые инструкции входит в состав среды выполнения .NET. Используя код IL, разработчики могут не думать об особенностях архитектуры CPU данного компьютера - эти особенности будут у
чтены JIT.
Откомпилированные из IL платформенно-зависимые инструкции JIT помещает в кэш-памяти, что очень сильно ускоряет работу приложения. Предположим, был вызван метод Bar() класса Foo. При первом вызове этого метода JIT откомпилирует относящийся к этому методу код IL в платформенно-зависимые инструкции. При повторных вызовах этого метода JIT уже не будет заниматься компиляцией, а просто возьмет уже готовый откомпилированный код из кэша в оперативной памяти.
Типы .NET и пространства имен .NET
Сборка (не важно, однофайловая или многофайловая) может содержать любое количество самых разных типов. В мире .NET тип - это общий термин, который может относиться к классам, структурам, интерфейсам, перечислениям и прочему. При создании приложения .NET (например, на языке C#) вам потребуется организовывать взаимодействие этих типов. Например, сборка может определять класс с несколькими интерфейсами; один интерфейс может принимать в качестве параметров только значения определенного перечисления.
У вас есть возможность использовать пространства имен при создании ваших собственных типов. Пространство имен - это логическая структура для организации имен, используемых в приложении .NET. Основное назначение пространств имен - предупредить возможные конфликты между именами в разных сборках.
Вот пример: вы создаете приложение типа Windows Forms, которое обращается к двум внешним сборкам. В каждой сборке есть тип с именем GoCart, при этом эти типы отличаются друг от друга. При написании кода вы можете точно указать, к какому именно типу и из какой сборки вы обращаетесь. Для этого достаточно к имени типа добавить имя соответствующего пространства имен: например, CustomVehicals.GoCart или SlowVehicals.GoCart. Более подробно мы разберем применение пространств имен ниже в этой главе.
Основы Common Language Runtime - среды выполнения .NET
После того как мы познакомились с типами, сборками, метаданными и IL, настало время рассмотреть среду выполнения .NET - CLR более подробно. Среду выполнения (runtime) можно рассматривать как набор служб, необходимых для работы блока программного кода. К таким службам можно отнести и требуемые библиотеки. Например, если вы создали приложение MFC, то в качестве компонента среды выполнения вам потребуется весьма объемистая библиотека времени выполнения Microsoft Foundation Classes - mfc42.dll. Программы на Visual Basic привязаны к такому компоненту среды выполнения, как библиотека msvbvm60.dll,
а программам на Java необходим большой набор файлов, входящих в состав виртуальной машины Java.
Своя среда выполнения требуется и приложениям .NET. Главное отличие этой среды выполнения от всех тех, которые были перечислены выше, заключается в том, что единая среда выполнения .NET используется приложениями, написанными на любых языках программирования .NET. Как уже говорилось выше, среда выполнения .NET носит официальное название Common Language Runtime (CLR).
Сама CLR состоит из двух главных компонентов. Первый компонент - это ядро среды выполнения, которое реализовано в виде библиотеки mscoree.dll. При обращении к приложению .NET mscoree.dll автоматически загружается в память, и, в свою очередь, эта библиотека управляет процессом загрузки в память сборки данного приложения. Ядро среды выполнения ответственно за множество задач. Оно занимается поиском физического местонахождения сборки, обнаружением внутри сборки запрошенного типа (класса, интерфейса, структуры и т. п.) на основе информации метаданных, компилирует IL в пла
тформенно-зависимые инструкции, выполняет проверки, связанные с обеспечением безопасности, - и этот перечень далеко не полон.
Второй главный компонент CLR - это библиотека базовых классов. Сама библиотека разбита на множество отдельных сборок, однако главная сборка библиотеки базовых классов представлена файлом mscorlib.dll. В библиотеке базовых классов содержится огромное количество типов для решения распространенных задач при создании приложения. Приложение .NET будет обязательно использовать сборку mscorlib.dll и по мере необходимости - другие сборки (как встроенные, так и создаваемые вами самими).
На рис. 1.3 представлен путь, который проходит исходный код приложения, прежде чем воплотиться в выполнение каких-либо действий на компьютере.
Стандартная система типов CTS
Мы уже говорили, что стандартная система типов (Common Type System, CTS) - это формальная спецификация, которая определяет, как какой-либо тип (класс, структура, интерфейс, встроенный тип данных и т. п.) должен быть определен для его правильного восприятия средой выполнения .NET. CTS определяет синтаксические конструкции (в качестве примера можно взять перегрузку операторов), которые могут поддерживаться, а могут и не поддерживаться конкретным языком программирования .NET. Если вы хотите создавать сборки, которые смогут использоваться всеми языками программирования .NET,
вам придется при создании типов следовать правилам Common Language Specification - CLS. А сейчас мы рассмотрим особенности тех типов, к которым применяется спецификация CTS.
Классы CTS
Концепция классов - краеугольный камень любого объектно-ориентированного программирования. Она поддерживается всеми языками программирования .NET. Класс (class) - это набор свойств, методов и событий, объединенных в единое целое. Как, наверное, вы и предполагали, в CTS предусмотрены абстрактные члены классов, что обеспечивает возможность применения полиморфизма в производных классах. Множественное наследование в CTS запрещено. Самые важные характеристики классов представлены в табл. 1.1.
Таблица 1.1. Самые важные характеристики классов CTS
Характеристика Ее смысл
Является ли класс "закрытым"? Закрытые классы не могут становиться базовыми для других классов
Предусмотрены ли в классе какие-либо интерфейсы? Интерфейс - это набор абстрактных членов, который обеспечивает связь между объектом и пользователем. В CTS в классе может быть любое количество интерфейсов
Является ли класс абстрактным? Объекты абстрактных классов создать невозможно. Единственное назначение абстрактных классов - выполнять роль базовых для других классов. За счет механизма наследования абстрактные классы обеспечивают производные классы общими наборами членов
Какова область видимости для данного класса? Для каждого класса должен быть определен атрибут области видимости (visibility). Как правило, значение этого атрибута определяет, можно ли обращаться к этому классу из внешних сборок или только из той, которая его содержит
Структуры CTS
Помимо классов в CTS также предусмотрена концепция структур (structures). Если вы работали с C, возможно, вы удивитесь, что этот пользовательский тип данных сохранился и в мире .NET (правда, надо отметить, что он немного изменился). В принципе, структуры можно грубо рассматривать как упрощенные разновидности классов (подробнее о различиях между классами и структурами будет рассказано в главе 2). Структуры CTS могут иметь любое количество конструкторов с параметрами (конструктор без параметров зарезервирован). С помощью конструкторов с параметрами вы можете установ
ить значение любого поля объекта структуры в момент создания этого объекта. Например:
// Определяем структуру C#
struct Baby
{
// В структуре могут быть поля:
public string name;
// В структуре можно определить конструкторы (но только c параметрами):
public Baby (string name)
{this.name = name; }
// В структурах могут быть определены методы:
public void Cry()
{ Console.WriteLine ("Waaaaaaaaaaah!!!"); }
public bool IsSpleeping() { return false; }
public bool IsChanged() { return false; }
}
А вот наша структура в действии:
// Добро пожаловать в мир малютки Макса!
Baby barnaBaby = new Baby ("Max");
Console.WriteLine ("Changed?: {0}", barnaBaby.IsChanged().ToString());
Console.WriteLine ("Sleeping?: {0}", barnaBaby.IsSleeping().ToString());
// А теперь Макс покажет нам свою подлинную сущность:
for(int i=0; i<10000; i++)
barnaBaby.Cry();
Все CTS-совместимые структуры произведены от единого базового класса System.ValueType. Этот базовый класс определяет структуру как тип данных для работы только со значениями, но не с ссылками. В структуре может быть любое количество интерфейсов. Однако структуры не могут быть унаследованы от остальных типов данных и они всегда являются "закрытыми" - то есть они не могут выступать в качестве базовых для целей наследования.
Интерфейсы CTS
Интерфейсы (interfaces) - это просто наборы абстрактных методов, свойств и определений событий. В отличие от классической технологии COM, интерфейсы .NET не являются производными от единого общего интерфейса, каким в мире COM был интерфейс IUnknown. В интерфейсах самих по себе смысла не очень много. Однако если мы знаем, что какой-либо класс реализует известный нам интерфейс, мы вправе требовать от этого класса определенной функциональности. При создании своего собственного интерфейса на .NET-совместимом языке программирования вы можете произвести этот интерфейс сраз
у от нескольких базовых интерфейсов. Подробнее про использование интерфейсов в программах на C# будет рассказано в главе 6.
Члены типов CTS
Как мы уже говорили, в классах и структурах может быть любое количество членов. Член (member) - это либо метод, либо свойство, либо поле, либо событие. Подробнее про члены типов в мире .NET будет рассказано в нескольких ближайших главах. Однако нам сейчас важно отметить, что для любого члена в .NET существует набор характеристик.
Например, любой член в .NET характеризуется своей областью видимости (public, private, protected и т. д.). Член можно объявить как абстрактный, чтобы воспользоваться возможностями полиморфизма при работе с производными классами. Члены могут быть статическими (static, такие члены могут совместно использоваться всеми объектами данного класса) и обычными - принадлежащими только одному объекту данного класса.
Перечисления CTS
Перечисление (enumeration) - это удобная программная конструкция, которая позволяет вам объединять пары имя - значение под указанным вами именем перечисления. Предположим, что вы создаете компьютерную игрушку, в которой играющий сможет выбирать из трех типов персонажей - волшебника (Wizard), воина (Fighter) или вора (Thief). В этой ситуации очень удобно будет воспользоваться перечислением:
// Перечисление C#:
enum PlayerType
{ Wizard=100, Fighter=200, Thief=300 };
В CTS все перечисления являются производными от единственного базового класса System.Enum. Как мы убедимся в будущем, этот базовый класс содержит множество полезных членов, которые помогут нам в извлечении (и выполнении прочих операций) с парами имя - значение.
Делегаты CTS
Делегаты (delegates) - в мире .NET это безопасный для типов эквивалент указателя на функцию в C. Однако между ними есть и существенное отличие. Делегат .NET - это уже не просто адрес в оперативной памяти, а класс, производный от базового класса MulticastDelegate. Делегаты очень полезны в тех ситуациях, когда вам нужно, чтобы одна сущность передала вызов другой сущности. Делегаты - это краеугольный камень в технологии обработки событий .NET (об этом - в главе 5).
Встроенные типы данных CTS
Конечно же, в .NET предусмотрен богатый набор встроенных типов данных. Помимо всего прочего, этот набор еще и един для всех языков программирования .NET. Названия типов данных в языках .NET могут выглядеть по-разному, но эти названия - всего лишь псевдонимы для встроенных системных типов данных .NET, определенных в библиотеке базовых типов. Перечень встроенных типов данных .NET представлен в табл. 1.2.
Таблица 1.2. Встроенные типы данных CTS
Встроенный тип данных .NET Название в Visual Basic.NET Название в C# Название в Managed C++
System.Byte Byte byte char
System.SByte Не поддерживается sbyte signed char
System.Int16 Short short short
System.Int32 Integer int int или long
System.Int64 Long long _int64
System.UInt16 Не поддерживается ushort unsigned short
System.UInt32 Не поддерживается uint unsigned int или unsigned long
System.UInt64 Не поддерживается ulong unsigned _int64
System.Single Single float float
System.Double Double double double
System.Object Object object Object*
System.Char Char char _wchar_t
System.String String string String*
System.Decimal Decimal decimal Decimal
System.Boolean Boolean bool bool
Как видно из таблицы, не все языки .NET могут работать с некоторыми встроенными типами данных CTS. Поэтому очень важно определить такой набор типов (и программных конструкций), с которым гарантированно смогут работать любые .NET-совместимые языки программирования. Такой набор есть, и он называется CLS.
Основы CLS
Нет необходимости доказывать, что одни и те же программные конструкции в разных языках выглядят абсолютно по-разному. Например, в C# объединение строк (конкатенация) производится с помощью оператора плюс (+), в то время как в Visual Basic для этой же цели используется амперсанд (&). А вот как выглядит в разных языках функция, не возвращающая значений:
' Функция (подпроцедура) VBasic, которая ничего не
' возвращает (возвращает значение типа void):
Public Sub Foo()
' Что-то делаем...
End Sub
// Такая же функция в C#:
public void Foo()
{
// Делаем то же самое...
}
Как мы уже видели, для среды выполнения .NET такая разница в синтаксисе абсолютно безразлична: все равно соответствующие компиляторы (в нашем случае vbc.exe и csc.exe) создадут одинаковый код IL. Однако языки программирования отличаются не только синтаксисом, но и возможностями. Например, в одних языках программирования разрешена перегрузка операторов, а в других - нет. Одни языки могут использовать беззнаковые (unsigned) типы данных, а в других такие типы данных не предусмотрены. Вывод очевиден - нам нужны некие единые правила для всех языков .NET. Если мы им следуем, то
гарантируется, что программные модули, написанные на разных языках, будут нормально взаимодействовать друг с другом. Такой набор правил определен в спецификации CLS (Common Language Specification).
Набор правил, определяемый CLS, не только гарантирует нормальное взаимодействие блоков кода, созданных на разных языках. Такой набор правил еще и определяет минимальные требования, которые предъявляются к любому .NET-совместимому компилятору. Необходимо помнить, что в CLS - это лишь часть тех возможностей, которые определены в CTS.
Правилам CLS должны удовлетворять и инструментальные средства среды разработки - если мы хотим обеспечить межъязыковое взаимодействие, они должны генерировать только такой код, который соответствует требованиям CLS. У каждого правила CLS есть простое название (например, CLS Rule 6 - правило CLS номер 6). Вот пример одного из правил (это самое важное правило - правило номер 1):
Правило 1. Правила CLS относятся только к тем частям типа, которые предназначены для взаимодействия за пределами сборки, в которой они определены.
Из этого правила явствует, что во внутренней логике при реализации какого-либо типа вы можете сколько угодно нарушать правила CLS - это ни на что не повлияет. Например, предположим, что вы создаете приложение .NET, которое взаимодействует с внешним миром с помощью трех классов, а в каждом из этих классов есть только одна функция. Правилам CLS в этом случае должны удовлетворять только три этих функции-члена (в отношении области видимости, соглашений об именовании, типов принимаемых параметров и т. д.). Во внутренней реализации этих функций, классов или приложения
в целом может быть сколько угодно отступлений от правил CLS - за пределами вашего программного модуля никто об этом никогда не узнает.
Конечно, в CLS существует не только правило 1, но и множество других правил. В CLS, к примеру, строгие требования предъявлены к представлению символьных значений, к определению перечислений, к использованию статических членов и т. д. Однако заучивать эти правила совершенно не обязательно (конечно, если вы не заняты созданием .NET-совместимого компилятора для своего собственного языка программирования). Если вам потребовалась дополнительная информация по CLS, произведите поиск в MSDN по словосочетанию "Collected CLS Rules".
Работа с пространствами имен
Мы закончили обзор среды выполнения .NET и теперь обратимся к особенностям библиотеки базовых классов .NET. Важность библиотек кода очевидна. Например, библиотека MFC определяет набор классов C++ для создания диалоговых окон, меню и панелей управления. В результате программисты могут не заниматься изобретением того, что давным-давно уже сделано до них, а сосредоточиться на уникальных аспектах создаваемого ими приложения. Аналогичные средства существуют и в Visual Basic, и в Java, и во всех остальных языках программирования.
Однако в отличие от MFC, Visual Basic и Java в C# не существует библиотеки базовых классов только для этого языка. Можно сказать, что библиотека базовых классов C# вообще не существует. Вместо этого разработчики на C# используют библиотеку базовых типов для всей среды .NET. А для лучшей организации типов внутри этой библиотеки используется концепция пространств имен.
Главное отличие от библиотек, привязанных к конкретному языку (типа MFC), заключается в том, что в любом .NET-совместимом языке используются те же самые типы и те же самые пространства имен, что и в C#. Вот три приложения (весьма напоминающих классическое Hello, World) на трех разных .NET-совместимых языках: C#, VB.NET и Managed C++ (MC++).
// Привет от C#
using System;
public class MyApp
{
public static void Main()
{
Console.WriteLine ("Hi from C#");
}
}
' Привет от VB.NET
Imports System
Public Module MyApp
Sub Main()
Console.WriteLine ("Hi from VB");
End Sub
End Module
// Привет от Managed C++
using namespace System;
// Обратите внимание! Среда выполнения .NET в C++ сама собой помещает глобальную
// функцию main внутрь определения класса
void main()
{
Console::WriteLine("Hi from MC++");
}
Обратите внимание, что если нужен класс Console, то в любом языке .NET используется одно и то же пространство имен System. Если не обращать внимания на синтаксические различия, то код приложений на разных языках .NET практически идентичен. Платформе .NET свойственно изящество единого стиля программирования.
Важнейшие пространства имен .NET
Эффективность работы программиста, использующего .NET, напрямую зависит от того, насколько он знаком с тем массивом типов, который определен в пространствах имен библиотеки базовых классов. Самое важное пространство имен в C# - это System. В нем определены классы, которые обеспечивают самые важные функции C#. Вам не удастся создать ни одно работоспособное приложение C# без использования этого пространства имен.
Пространство имен - это просто способ организации типов (классов, перечислений, интерфейсов, делегатов и структур) в единую группу. Конечно, обычно в одном пространстве имен объединяются взаимосвязанные типы. Например, тип System.Drawing содержит набор типов, которые призваны помочь вам в организации вывода изображений на графическое устройство. В .NET предусмотрены пространства имен для организации типов для работы с базами данными, Web, многопоточностью, защитой данных и множества других задач. В табл. 1.3 приведены некоторые (далеко не все) пространства имен .NET.
Таблица 1.3. Пример пространства имен .NET
Пространство имен .NET Назначение
System Внутри - множество низкоуровневых классов для работы с простыми типами, выполнения математических операций, сборки мусора и т. п.
System.Collections Для работы с контейнерными объектами, такими как ArrayList, Queue, SortedList
System.Data Для обращений к базам данных. В книге этой теме посвящена
System.Data.Common, System.Data.OleDb, System.Data.SqlClient специальная глава
System.Diagnostics В этом пространстве имен содержатся многочисленные типы, используемые .NET-совместимыми языками для трассировки и отладки программного кода
System.Drawing Типы для примитивов GDI+ - растровых изображений,
System.Drawing.Drawing2D шрифтов, значков, поддержи печати. Предусмотрены также
System.Drawing.Printing специальные классы для вывода более сложных изображений
System.IO Как следует из названия, в этом пространстве имен объединены типы, отвечающие за операции ввода-вывода - в файл, буфер и т. п.
System.Net Это пространство имен (как и все остальные, связанные с ним) содержит типы, относящиеся к передаче данных по сети (запрос - ответ, создание сокетов и т. п.)
System.Reflection Классы, предназначенные для обнаружения, создания и вызова
System.Reflection.Emit во время выполнения пользовательских типов
System.Runtime.InteropServices Средства для взаимодействия с "традиционным" кодом
System.Runtime.Remoting (Win32 DLL, COM-серверы) и типы, используемые для удаленного доступа (например, по коммутируемым соединениям)
System.Security В мире .NET средства обеспечения безопасности интегрированы как со средой выполнения, так и с библиотекой базовых типов. В этом пространстве имен находятся классы для работы с разрешениями, криптографией и т. п.
System.Threading Скорее всего, вы уже угадали - это пространство имен для типов, которые используются при работе с потоками (например, Mutex, Thread или Timeout)
System.Web Классы, которые предназначены для использования в web-приложениях, включая ASP.NET
System.Windows.Forms Классы для работы с элементами интерфейса Windows - окнами, элементами управления и прочим
System.XML Множество классов для работы с данными в формате XML
Использование пространств имен в коде приложения
Как мы помним, пространство имен - это средство для логической группировки типов. С человеческой точки зрения выражение System.Console означает тип Console в пространстве имен System. Однако с точки зрения среды выполнения .NET System.Console - это единая сущность, к которой можно обратиться разными способами.
Слово using нужно только вам - так проще будет обращаться к типам в конкретном пространстве имен. Если по каким-либо причинам использовать слово using вы не хотите, вполне можно обойтись и без него. Давайте рассмотрим это на примере. Предположим, что вы создаете обычное оконное приложение Windows, которое должно представлять на круговой диаграмме информацию, извлекаемую из базы данных. Кроме того, вам еще нужно поместить на главную форму своего приложения растровый рисунок с логотипом компании. Немного подумав, мы можем определить, что в нашем приложении нам потр
ебуются классы из следующих пространств имен:
//Пространства имен для использования в нашем приложении
using System; //Без главного пространства имен не обойтись
using System.Drawing; //для вывода изображений
using System.Windows.Forms; //для элементов интерфейса
using System.Data; //для доступа к базе данных using System.OleDb; //если к базе данных мы обращаемся по OLE DB
После того как мы определим использование конкретного пространства имен (с использованием ключевого слова using), мы можем обращаться к типам, содержащимся в этом пространстве. Например, если нам потребовалось создать экземпляр класса Bitmap (определенном в пространстве имен System.Drawing), код может быть таким:
//Явно указываем использование пространства имен:
using System.Drawing;
class MyClass
{
public void DoIt()
{
//Создаем растровое изображение 20 на 20 пикселов
Bitmap bm = new Bitmap (20, 20);
...
}
}
Поскольку мы явно указали использование пространства имен System.Drawing с помощью ключевого слова using, компилятор сможет понять, что класс Bitmap - это член данного пространства имен. Если в примере, приведенном выше, мы опустим строку со словом using, мы получим сообщение компилятора об ошибке. Однако можно обойтись и без using, если использовать полное имя класса, как в следующем примере:
//Обратите внимание - никаких указаний на пространства имен!
class MyClass
{
public void DoIt()
{
//Используем полное имя
System.Drawing.Bitmap bm = new.System.Drawing.Bitmap (20, 20);
...
}
}
Главная идея, я думаю, понятна: если вы явно указываете используемое пространство имен, строки при обращении к классам этого пространства имен получаются гораздо меньшего размера.
Обращения к внешним сборкам
Помимо того что вы можете явно указать используемое пространство имен с помощью ключевого слова using, иногда вам может потребоваться еще и явно указать физическое местонахождение сборки с необходимым кодом IL. Многие важнейшие пространства имен .NET физически связаны с файлом mscorlib.dll. Типы пространства имен System.Drawing физически "живут" внутри файла System.Drawing.dll. По умолчанию встроенные сборки .NET находятся в подкаталоге :\WINNT\Microsoft.NET\Framework\, как показано на рис. 1.4.
В зависимости от того, какие средства разработки вы используете для создания приложений .NET, существует много способов сообщить компилятору, какие именно сборки вы собираетесь задействовать во время процесса компиляции. Про эти способы будет рассказано ниже.
Если вам стало немного не по себе от мысли о том, сколько информации о пространствах имен и о типах вам придется осваивать, помните, что помнить все типы всех пространств имен совершенно незачем. Если вы создаете консольное приложение, вам можно забыть о всех типах System.Windows.Forms и System.Drawing (а, скорее всего, и о многих других). Если же вы разрабатываете редактор изображений, вам вряд ли потребуются интерфейсы для доступа к базам данных. Типы пространств имен можно осваивать постепенно, по мере необходимости.
Как получить дополнительную информацию о пространствах имен и типах
Во всех главах этой книги мы будем осваивать различные возможности платформы .NET, используя пространства имен и содержащиеся в них типы. Книга вряд ли стала бы лучше, если бы мы рассмотрели в ней все без исключения типы во всех встроенных пространствах имен. Вы должны уметь находить информацию о нужных вам типах и пространствах имен самостоятельно, тем более что инструментов для этой цели много. Вот их краткий перечень:
документация .NET SDK (в MSDN);
утилита ILDasm.exe;
web-приложение ClassView;
графическое приложение WinCV;
ObjectBrowser, входящий в комплект Visual Studio.NET.
Вряд ли вас нужно учить тому, как использовать MSDN (намекнем только, что внутри Visual Studio.NET можно попробовать начать с кнопки F1). А вот про все остальные утилиты стоит поговорить подробнее. Начнем с ILDasm.exe, ClassView и WinCV. Все эти утилиты поставляются вместе с .NET SDK.
ILDasm.exe
Официальное название ILDasm.exe звучит как Intermediate Language Disassembler utility (утилита дизассемблирования промежуточного языка). Эта утилита позволяет просмотреть содержимое любой сборки .NET (файла DLL или EXE) - ее манифест, метаданные типов и инструкции IL. При этом все операции производятся с использованием дружественного графического интерфейса. Просто запустите ILDasm.exe и через меню File 4 Open откройте нужную сборку. В порядке демонстрации мы откроем сборку mscorlib.dll (рис. 1.5). Путь к открытой нами сборке будет показан в заголовке окна ILDasm.
Как мы видим, структура сборки представлена в самом обычном формате с деревом и узлами. Каждый метод, свойство, вложенный класс, как и все остальные типы, представлены специальными значками (в текстовом дампе дизассемблера эти значки будут заменены на аббревиатуры, состоящие из трех символов). Самые распространенные значки и соответствующие им аббревиатуры ILDasm приведены в табл. 1.4.
C# и платформа .NET. Библиотека программиста. / Э. Троелсен - СПб: Питер, 2003. - 800 с.
|