Текст
                    ЯЗЫК ПРОГРАММИРОВАНИЯ
С#2010
И ПЛАТФОРМА .NET 4
5-е издание


PRO C# 2010 AND THE .NET 4 PLATFORM Fifth Edition Andrew Troelsen Apress*
ЯЗЫК ПРОГРАММИРОВАНИЯ С#2010 И ПЛАТФОРМА .NET 4 5-е издание Эндрю Троелсен Москва • Санкт-Петербург • Киев 2011
ББК 32.973.26-018.2.75 Т70 УДК 681.3.07 Издательский дом "Вильяме" Зав. редакцией С.Н. Тригуб Перевод с английского Я.П. Волковой, А.А. Моргунова, Н.А. Мухина Под редакцией Ю.Н. Артпеменко По общим вопросам обращайтесь в Издательский дом "Вильяме" по адресу: info@williamspublishing.com, http://www.williamspublishing.com Трое л сен, Эндрю. Т70 Язык программирования С# 2010 и платформа .NET 4.0, 5-е изд. : Пер. с англ. — М. : ООО "И.Д. Вильяме", 2011. — 1392 с. : ил. — Парал. тит. англ. ISBN 978-5-8459-1682-2 (рус.) ББК 32.973.26-018.2.75 Все названия программных продуктов являются зарегистрированными торговыми марками соответствующих фирм. Никакая часть настоящего издания ни в каких целях не может быть воспроизведена в какой бы то ни было форме и какими бы то ни было средствами, будь то электронные или механические, включая фотокопирование и запись на магнитный носитель, если на это нет письменного разрешения издательства APress, Berkeley, CA. Authorized translation from the English language edition published by APress, Inc., Copyright © 2010. All rights reserved. No part of this work may be reproduced or transmitted In any form or by any means, electronic or mechanical, Including photocopying, recording, or by any Information storage or retrieval system, without the prior written permission of the copyright owner and the publisher. Trademarked names may appear in this book. Rather than use a trademark symbol with every occurrence of a trademarked name, we use the names only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark. Russian language edition is published by Williams Publishing House according to the Agreement with R&I Enterprises International, Copyright © 2011. Научно-популярное издание Эндрю Троелсен Язык программирования С# 2010 и платформа .NET 4.0 5-е издание Верстка Т.Н. Артпеменко Художественный редактор С.А. Чернокозинский Подписано в печать 28.10.2010. Формат 70x100/16. Гарнитура Times. Печать офсетная. Усл. печ. л. 112,23. Уч.-изд. л. 87,1. Тираж 2000 экз. Заказ № 24430. Отпечатано по технологии CtP в ОАО "Печатный двор" им. А. М. Горького 197110, Санкт-Петербург, Чкаловский пр., 15. ООО "И. Д. Вильяме", 127055, г. Москва, ул. Лесная, д. 43, стр. 1 ISBN 978-5-8459-1682-2 (рус.) © Издательский дом "Вильяме", 2011 ISBN 978-1-43-022549-2 (англ.) © by Andrew TYoelsen, 2010
Оглавление Часть I. Общие сведения о языке С# и платформе .NET 43 Глава 1. Философия .NET 44 Глава 2. Создание приложений на языке С# 80 Часть II. Главные конструкции программирования на С# 105 Глава 3. Главные конструкции программирования на С#: часть Г 106 Глава 4. Главные конструкции программирования на С#: часть ГГ 150 Глава 5. Определение инкапсулированных типов классов 185 Глава 6. Понятия наследования и полиморфизма 231 Глава 7. Структурированная обработка исключений 265 Глава 8. Время жизни объектов 292 Часть III. Дополнительные конструкции программирования на С# 319 Глава 9. Работа с интерфейсами 320 Глава 10. Обобщения 356 Глава 11. Делегаты, события и лямбда-выражения 386 Глава 12. Расширенные средства языка С# 421 Глава 13. L1NQ to Objects 463 Часть IV. Программирование с использованием сборок .NET 493 Глава 14. Конфигурирование сборок .NET 494 Глава 15. Рефлексия типов, позднее связывание и программирование с использованием атрибутов 542 Глава 16. Процессы, домены приложений и контексты объектов 582 Глава 17. Язык C1L и роль динамических сборок 607 Глава 18. Динамические типы и исполняющая среда динамического языка 648 Часть V. Введение в библиотеки базовых классов .NET 669 Глава 19. Многопоточность и параллельное программирование 670 Глава 20. Файловый ввод-вывод и сериализация объектов 711 Глава 21. ADO.NET, часть Г: подключенный уровень 754 Глава 22. ADO.NET, часть ГГ: автономный уровень 804 Глава 23. ADO.NET, часть ПГ: Entity Framework 857 Глава 24. Введение в UNQ to XML 891 Глава 25. Введение в Windows Communication Foundation 906 Глава 26. Введение в Windows Workflow Foundation 4.0 961 Часть VI. Построение настольных пользовательских приложений с помощью WPF 995 Глава 27. Введение в Windows Presentation Fbundation и XAML 996 Глава 28. Программирование с использованием элементов управления WPF 1048 Глава 29. Службы визуализации графики WPF 1103 Глава 30. Ресурсы, анимация и стили WPF 1137 Глава 31. Шаблоны элементов управления WPF и пользовательские элементы управления 1170 Часть VII. Построение веб-приложений с использованием ASP.NET 1213 Глава 32. Построение веб-страниц ASP.NET 1214 Глава 33. Веб-элементы управления, мастер-страницы и темы ASP.NET 1257 Глава 34. Управление состоянием в ASP.NET 1294 Часть VIII. Приложения 1327 Приложение А. Программирование с помощью Windows Forms 1328 Приложение Б. Независимая от платформы разработка .NET-приложений с помощью Mono 1369 Предметный указатель 1386
Содержание Об авторе 31 О техническом редакторе 31 Благодарности 31 Введение 32 Автор и читатели — одна команда 33 Краткий обзор содержания 33 Исходный код примеров 42 От издательства 42 Часть I. Общие сведения о языке С# и платформе .NET 43 Глава 1. Философия .NET 44 Предыдущее состояние дел 44 Подход с применением языка С и API-интерфейса Windows 45 Подход с применением языка C++ и платформы MFC 45 Подход с применением Visual Basic 6.0 45 Подход с применением Java 46 Подход с применением СОМ 46 Сложность представления типов данных СОМ 47 Решение .NET 48 Главные компоненты платформы .NET (CLR, CTS и CLS) 49 Роль библиотек базовых классов 50 Что привносит язык С# 50 Другие языки программирования с поддержкой .NET 52 Жизнь в многоязычном окружении 54 Что собой представляют сборки в .NET 54 Однофайловые и многофайловые сборки 56 Роль CIL 56 Преимущества CIL 58 Компиляция CIL-кода в инструкции, ориентированные на конкретную платформу 58 Роль метаданных типов в .NET 59 Роль манифеста сборки 60 Что собой представляет общая система типов (CTS) 60 Типы классов 61 Типы интерфейсов 61 Типы структур 62 Типы перечислений 62 Типы делегатов 63 Члены типов 63 Встроенные типы данных 63 Что собой представляет общеязыковая спецификация (CLS) 64 Забота о соответствии правилам CLS 66 Что собой представляет общеязыковая исполняющая среда (CLR) 66 Различия между сборками, пространствами имен и типами 67 Роль корневого пространства Microsoft 71 Получение доступа к пространствам имен программным образом 71 Добавление ссылок на внешние сборки 73 Изучение сборки с помощью утилиты ildasm. exe 74 Просмотр CIL-кода 74 Просмотр метаданных типов 74 Просмотр метаданных сборки (манифеста) 74 Изучение сборки с помощью утилиты Reflector 75
Содержание 7 Развертывание исполняющей среды .NET 76 Клиентский профиль исполняющей среды .NET 77 Не зависящая от платформы природа .NET 77 Резюме 79 Глава 2. Создание приложений на языке С# 80 Роль комплекта . NET Framework 4.0 SDK 80 Окно командной строки в Visual Studio 2010 81 Создание приложений на С# с использованием csc.exe 81 Указание целевых входных и выходных параметров 82 Добавление ссылок на внешние сборки 84 Добавление ссылок на несколько внешних сборок 84 Компиляция нескольких файлов исходного кода 85 Работа с ответными файлами в С# 85 Создание приложений .NET с использованием Notepad++ 87 Создание приложений.NET с помощью SharpDevelop 88 Создание простого тестового проекта 89 Создание приложений .NET с использованием Visual С# 2010 Express 90 Некоторые уникальные функциональные возможности Visual С# 2010 Express 91 Создание приложений .NET с использованием Visual Studio 2010 92 Некоторые уникальные функциональные возможности Visual Studio 2010 92 Ориентирование на .NET Framework в диалоговом окне New Project 93 Использование утилиты Solution Explorer 93 Утилита Class View 95 Утилита Obj ect Browser 9 5 Встроенная поддержка рефакторинга программного кода 96 Возможности для расширения и окружения кода 98 Утилита Class Designer 100 Интегрируемая система документации . NET Framework 4.0 102 Резюме 104 Часть II. Главные конструкции программирования на С# Ю5 Глава 3. Главные конструкции программирования на С#: часть I 106 Разбор простой программы на С# 106 Варианты метода Ma i n () 108 Спецификация кода ошибки в приложении 109 Обработка аргументов командной строки 110 Указание аргументов командной строки в Visual Studio 2010 111 Интересное отклонение от темы: некоторые дополнительные члены класса System.Environment 112 Класс System.Console 113 Базовый ввод-вывод с помощью класса Console 114 Форматирование вывода, отображаемого в окне консоли 115 Форматирование числовых данных г 116 Форматирование числовых данных в приложениях, отличных от консольных 117 Системные типы данных и их сокращенное обозначение в С# 117 Объявление и инициализация переменных 119 Внутренние типы данных и операция new 120 Иерархия классов типов данных 121 Члены числовых типов данных 123 Члены System.Boolean 123 Члены System.Char 124 Синтаксический разбор значений из строковых данных 125
8 Содержание Типы System.DateTimeи System.TimeSpan 125 Пространство имен System.Numerics в .NET4.0 126 Работа со строковыми данными 127 Базовые операции манипулирования строками 128 Конкатенация строк 129 Управляющие последовательности символов 130 Определение дословных строк 131 Строки и равенство 131 Неизменная природа строк 132 Тип System.Text.StringBuilder 133 Сужающие и расширяющие преобразования типов данных 135 Перехват сужающих преобразований данных 137 Настройка проверки на предмет возникновения условий переполнения в масштабах проекта 139 Ключевое слово unchecked 139 Роль класса System. Convert 140 Неявно типизированные локальные переменные 140 Ограничения, связанные с неявно типизированными переменными 142 Неявно типизированные данные являются строго типизированными 143 Польза от неявно типизированных локальных переменных 144 Итерационные конструкции в С# 144 Цикл for 145 Цикл f oreach 145 Использование var в конструкциях foreach 146 Конструкции whilendo/while 146 Конструкции принятия решений и операции сравнения 147 Оператор if /else 147 Оператор switch 148 Резюме 149 Глава 4. Главные конструкции программирования на С#: часть II 150 Методы и модификаторы параметров 150 Стандартное поведение при передаче параметров 151 Модификатор out 152 Модификатор г е f 153 Модификатор par ams 154 Определение необязательных параметров 156 Вызов методов с использованием именованных параметров 157 Перегрузка методов 158 Массивы в С# 160 Синтаксис инициализации массивов в С # 161 Неявно типизированные локальные массивы 162 Определение массива объектов 163 Работа с многомерными массивами 163 Использование массивов в качестве аргументов и возвращаемых значений 165 Базовый класс System. Array 165 Тип enum 167 Управление базовым типом, используемым для хранения значений перечисления 168 Объявление переменных типа перечислений 168 Тип System.Enum 169 Динамическое обнаружение пар "имя/значение" перечисления 170 Типы структур 172 Создание переменных типа структур 173
Содержание 9 Типы значения и ссылочные типы 174 Типы значения, ссылочные типы и операция присваивания 175 Типы значения, содержащие ссылочные типы 177 Передача ссылочных типов по значению 178 Передача ссылочных типов по ссылке 179 Заключительные детали относительно типов значения и ссылочных типов 180 Нулевые типы в С # 181 Работа с нулевыми типами 183 Операция?? 184 Резюме 184 Глава 5. Определение инкапсулированных типов классов 185 Знакомство с типом класса С# 185 Размещение объектов с помощью ключевого слова new 187 Понятие конструктора 188 Роль конструктора по умолчанию 188 Определение специальных конструкторов 189 Еще раз о конструкторе по умолчанию 190 Роль ключевого слова this 191 Построение цепочки вызовов конструкторов с использованием this 193 Обзор потока конструктора 195 Еще раз об необязательных аргументах 196 Понятие ключевого слова static 197 Определение статических методов 198 Определение статических полей данных 198 Определение статических конструкторов 201 Определение статических классов 202 Основы объектно-ориентированного программирования 203 Роль инкапсуляции 204 Роль наследования 204 Роль полиморфизма 206 Модификаторы доступа С# 207 Модификаторы доступа по умолчанию 208 Модификаторы доступа и вложенные типы 208 Первый принцип: службы инкапсуляции С# 209 Инкапсуляция с использованием традиционных методов доступа и изменения 210 Инкапсуляция с использованием свойств .NET 212 Использование свойств внутри определения класса 214 Внутреннее представление свойств 215 Управление уровнями видимости операторов get/set свойств 217 Свойства, доступные только для чтения и только для записи 218 Статические свойства 218 Понятие автоматических свойств 219 Взаимодействие с автоматическими свойствами 221 Замечания относительно автоматических свойств и значений по умолчанию 221 Понятие синтаксиса инициализации объектов 223 Вызов специальных конструкторов с помощью синтаксиса инициализации 224 Инициализация вложенных типов 225 Работа с данными константных полей 226 Понятие полей только для чтения 228 Статические поля только для чтения 228 Понятие частичных типов 229 Резюме 230
10 Содержание Глава 6. Понятия наследования и полиморфизма 231 Базовый механизм наследования 231 Указание родительского класса для существующего класса 232 О множественном наследовании 234 Ключевое слово sealed 234 Изменение диаграмм классов Visual Studio 235 Второй принцип ООП: подробности о наследовании 236 Управление созданием базового класса с помощью ключевого слова base 238 Хранение фамильных тайн: ключевое слово protected 240 Добавление запечатанного класса 241 Реализация модели включения/делегации 242 Определения вложенных типов 243 Третий принцип ООП: поддержка полиморфизма в С# 245 Ключевые слова virtual и override 245 Переопределение виртуальных членов в Visual Studio 2010 247 Запечатывание виртуальных членов 248 Абстрактные классы 249 Полиморфный интерфейс 250 Сокрытие членов 253 Правила приведения к базовому и производному классу 255 Ключевое слово as 257 Ключевое слово is 257 Родительский главный класс System.Object 258 Переопределение System.Object.ToStringO 261 Переопределение System.Object.Equals() 261 Переопределение System.Object.GetHashCodeO 262 Тестирование модифицированного класса Ре г s on 263 Статические члены System.Object 264 Резюме 264 Глава 7. Структурированная обработка исключений 265 Ода ошибкам и исключениям 265 Роль обработки исключений в .NET 266 Составляющие процесса обработки исключений в .NET 267 Базовый класс System. Exception 268 Простейший пример 269 Генерация общего исключения 271 Перехват исключений 272 Конфигурирование состояния исключения 273 Свойство TargetSite 273 Свойство StackTrace 274 Свойство НеlpLink 275 Свойство Data 275 Исключения уровня системы (System. SystemException) 277 Исключения уровня приложения (System. ApplicationException) 278 Создание специальных исключений, способ первый 278 Создание специальных исключений, способ второй 280 Создание специальных исключений, способ третий 281 Обработка многочисленных исключений 282 Общие операторы catch 284 Передача исключений 285 Внутренние исключения 286 Блок finally 287 Какие исключения могут выдавать методы 287
Содержание 11 К чему приводят необрабатываемые исключения 288 Отладка необработанных исключений с помощью Visual Studio 289 Несколько слов об исключениях, связанных с поврежденным состоянием 290 Резюме 291 Глава 8. Время жизни объектов 292 Классы, объекты и ссылки 292 Базовые сведения о времени жизни объектов 293 CIL-код, генерируемый для ключевого слова new 294 Установка объектных ссылок в пи 11 296 Роль корневых элементов приложения 297 Поколения объектов 298 Параллельная сборка мусора в версиях .NET 1.0 — .NET 3.5 299 Фоновая сборка мусора в версии . NET 4.0 300 Тип System. GC 300 Принудительная активизация сборки мусора 302 Создание финализируемых объектов 304 Переопределение System.Object .Finalize () 305 Описание процесса финализации 307 Создание высвобождаемых объектов 307 Повторное использование ключевого слова using в С# 310 Создание финализируемых и высвобождаемых типов 311 Формализованный шаблон очистки 312 Отложенная инициализация объектов 314 Настройка процесса создания данных L a z у о 317 Резюме 318 Часть III. Дополнительные конструкции программирования на С# зш Глава 9. Работа с интерфейсами 320 Что собой представляют типы интерфейсов 320 Сравнение интерфейсов и абстрактных базовых классов 321 Определение специальных интерфейсов 324 Реализация интерфейса 326 Вызов членов интерфейса на уровне объектов 328 Получение ссылок на интерфейсы с помощью ключевого слова a s 329 Получение ссылок на интерфейсы с помощью ключевого слова i s 329 Использование интерфейсов в качестве параметров 330 Использование интерфейсов в качестве возвращаемых значений 332 Массивы типов интерфейсов 332 Реализация интерфейсов с помощью Visual Studio 2010 333 Устранение конфликтов на уровне имен за счет реализации интерфейсов явным образом 334 Проектирование иерархий интерфейсов 337 Множественное наследование в случае типов интерфейсов 338 Создание перечислимых типов (IE numerable и I Enumerator) 340 Создание методов итератора с помощью ключевого слова yield 343 Создание именованного итератора 344 Внутреннее представление метода итератора 345 Создание клонируемых объектов (ICloneable) 346 Более сложный пример клонирования 348 Создание сравнимых объектов (IComparable) 350. Указание множества критериев для сортировки (I Compare г) 353 Использование специальных свойств и специальных типов для сортировки 354 Резюме 355
12 Содержание Глава 10. Обобщения 356 Проблемы, связанные с необобщенными коллекциями 356 Проблема производительности 358 Проблемы с безопасностью типов 362 Роль параметров обобщенных типов 365 Указание параметров типа для обобщенных классов и структур 366 Указание параметров типа для обобщенных членов 367 Указание параметров типов для обобщенных интерфейсов 367 Пространство имен System.Collections.Generic 369 Синтаксис инициализации коллекций 370 Работа с классом List<T> 371 Работа с классом Stack<T> 373 Работа с классом Queue<T> 374 Работа с классом SortedSet<T> 375 Создание специальных обобщенных методов 376 Выведение параметра типа 378 Создание специальных обобщенных структур и классов 379 Ключевое слово default в обобщенном коде 380 Обобщенные базовые классы 381 Ограничение параметров типа 382 Примеры использования ключевого слова where 383 Недостаток ограничений операций 384 Резюме 385 Глава 11. Делегаты, события и лямбда-выражения 386 Понятие типа делегата .NET 386 Определение типа делегата в С# 387 Базовые классы System.MulticastDelegate и System.Delegate 389 Простейший пример делегата 391 Исследование объекта делегата 392 Отправка уведомлений о состоянии объекта с использованием делегатов 393 Включение группового вызова 396 Удаление целей из списка вызовов делегата 397 Синтаксис групповых преобразований методов 398 Понятие ковариантности делегатов 400 Понятие обобщенных делегатов 402 Эмуляция обобщенных делегатов без обобщений 403 Понятие событий С# 404 Ключевое слово event 405 "За кулисами" событий 406 Прослушивание входящих событий 407 Упрощенная регистрация событий с использованием Visual Studio 2010 408 Создание специальных аргументов событий 409 Обобщенный делегат Event Handle r<T> 410 Понятие анонимных методов С# 411 Доступ к локальным переменным 413 Понятие лямбда-выражений 413 Анализ лямбда-выражения 416 Обработка аргументов внутри множества операторов 417 Лямбда-выражения с несколькими параметрами и без параметров 418 Усовершенствование примера PrimAndProperCarEvents за счет использования лямбда-выражений 419 Резюме 419
Содержание 13 Глава 12. Расширенные средства языка С# 421 Понятие методов-индексаторов 421 Индексация данных с использованием строковых значений 423 Перегрузка методов-индексаторов 424 Многомерные индексаторы 425 Определения индексаторов в интерфейсных типах 425 Понятие перегрузки операций 426 Перегрузка бинарных операций 427 А как насчет операций += и -=? 429 Перегрузка унарных операций 429 Перегрузка операций эквивалентности 430 Перегрузка операций сравнения 431 .внутреннее представление перегруженных операций 432 Финальные соображения относительно перегрузки операций 433 Понятие преобразований пользовательских типов 434 Числовые преобразования 434 Преобразования между связанными типами классов 434 Создание специальных процедур преобразования 435 Дополнительные явные преобразования типа Square 437 Определение процедур неявного преобразования 438 Внутреннее представление процедур пользовательских преобразований 439 Понятие расширяющих методов 440 Понятие частичных методов 448 Понятие анонимных типов 450 Анонимные типы, содержащие другие анонимные типы 454 Работа с типами указателей 455 Ключевое слово un s a f e 456 Работа с операциями * и & 458 Небезопасная и безопасная функция обмена значений 459 Доступ к полям через указатели (операция ->) 459 Ключевое слово stackalloc 460 Закрепление типа ключевым словом fixed 460 Ключевое слово sizeof 461 Резюме 462 Глава 13. LINQ to Objects 463 Программные конструкции, специфичные для LINQ 463 Неявная типизация локальных переменных 464 Синтаксис инициализации объектов и коллекций 464 Лямбда-выражения 465 Расширяющие методы 466 Анонимные типы 466 Роль LINQ 467 Выражения LINQ строго типизированы 468 Основные сборки LINQ 468 Применение запросов LINQ к элементарным массивам 469 Решение без использования LINQ 470 Рефлексия результирующего набора LINQ 471 LINQ и неявно типизированные локальные переменные 472 LINQ и расширяющие методы 473 Роль отложенного выполнения 474 Роль немедленного выполнения 475 Возврат результата запроса LINQ 475 Возврат результатов LINQ через немедленное выполнение 476
14 Содержание Применение запросов LINQ к объектам коллекций 477 Доступ к содержащимся в контейнере подобъектам 478 Применение запросов LINQ к необобщенным коллекциям 478 Фильтрация данных с использованием OfType<T>() 479 Исследование операций запросов LINQ 480 Базовый синтаксис выборки 481 Получение подмножества данных 482 Проекция новых типов данных 482 Получение счетчиков посредством Enumerable • 484 Обращение результирующих наборов 484 Выражения сортировки 484 LINQ как лучшее средство построения диаграмм 485 Исключение дубликатов 486 Агрегатные операции LINQ 486 Внутреннее представление операторов запросов LINQ 487 Построение выражений запросов с использованием операций запросов 488 Построение выражений запросов с использованием типа Enumerable и лямбда-выражений 488 Построение выражений запросов с использованием типа Enumerable и анонимных методов 490 Построение выражений запросов с использованием типа Enumerable и низкоуровневых делегатов 490 Резюме 491 Часть IV. Программирование с использованием сборок .NET 493 Глава 14. Конфигурирование сборок .NET 494 Определение специальных пространств имен 494 Устранение конфликтов на уровне имен за счет использования полностью уточненных имен 496 Устранение конфликтов на уровне имен за счет использования псевдонимов 497 Создание вложенных пространств имен 498 Пространство имен, используемое по умолчанию в Visual Studio 2010 499 Роль сборок .NET 500 Сборки повышают возможность повторного использования кода 500 Сборки определяют границы типов 501 Сборки являются единицами, поддерживающими версии 501 Сборки являются самоописываемыми 501 Сборки поддаются конфигурированию 501 Формат сборки .NET 502 Заголовок файла Windows 502 Заголовок файла CLR 504 CIL-код, метаданные типов и манифест сборки 505 Необязательные ресурсы сборки 505 Однофайловые и многофайловые сборки 505 Создание и использование однофайловой сборки 506 Исследование манифеста 510 Исследование CIL-кода 512 Исследование метаданных типов 512 Создание клиентского приложения на С# 513 Создание клиентского приложения на Visual Basic 514 Межъязыковое наследование в действии 515 Создание и использование многофайловой сборки 516 Исследование файла uf о .netmodule 517
Содержание 15 Исследование файла airvehiсles .dll 518 Использование многофайловой сборки 518 Приватные сборки 519 Идентификационные данные приватной сборки 520 Процесс зондирования 520 Конфигурирование приватных сборок 521 Конфигурационные файлы и Visual Studio 2010 522 Разделяемые сборки 524 Строгие имена 524 Генерирование строгих имен в командной строке 526 Генерирование строгих имен с помощью Visual Studio 2010 528 Установка сборок со строгими именами в GAC 529 Просмотр содержимого GAC с помощью проводника Windows 530 Использование разделяемой сборки 531 Исследование манифеста SharedCarLibClient 532 Конфигурирование разделяемых сборок 533 Фиксация текущей версии разделяемой сборки 533 Создание разделяемой сборки версии 2.0.0.0 533 Динамическое перенаправление на конкретную версию разделяемой сборки 536 Сборки политик издателя 537 Отключение политик издателя 538 Элемент <codeBase> 538 Пространство имен System. Configuration 540 Резюме 541 Глава 15. Рефлексия типов, позднее связывание и программирование с использованием атрибутов 542 Необходимость в метаданных типов 542 Просмотр (части) метаданных перечисления EngineState 543 Просмотр (части) метаданных типа Саг 544 Изучение блока TypeRef 546 Просмотр метаданных самой сборки 546 Просмотр метаданных внешних сборок, на которые имеются ссылки в текущей сборке 546 Просмотр метаданных строковых литералов 547 Рефлексия 547 Класс System.Type 548 Получение информации о типе с помощью System. Ob j ect. GetType () 549 Получение информации о типе с помощью typeof () 549 Получение информации о типе с помощью System. Туре . GetType () 549 Создание специальной программы для просмотра метаданных 550 Рефлексия методов 550 Рефлексия полей и свойств 551 Рефлексия реализуемых интерфейсов 552 Отображение различных дополнительных деталей 552 Реализация метода Ma in () 552 Рефлексия обобщенных типов 554 Рефлексия параметров и возвращаемых значений методов 554 Динамически загружаемые сборки 556 Рефлексия разделяемых сборок 558 Позднее связывание 560 Класс System.Activator 560 Вызов методов без параметров 562 Вызов методов с параметрами 563 Роль атрибутов .NET 564
16 Содержание Потребители атрибутов 565 Применение предопределенных атрибутов в С# 565 Сокращенное обозначение атрибутов в С# 567 Указание параметров конструктора для атрибутов 567 Атрибут [Obsolete] в действии 567 Создание специальных атрибутов 568 Применение специальных атрибутов 569 Синтаксис именованных свойств 569 Ограничение использования атрибутов 570 Атрибуты уровня сборки и модуля 571 Файл Ass emb lylnfo.cs, генерируемый Visual Studio 2010 572 Рефлексия атрибутов с использованием раннего связывания 572 Рефлексия атрибутов с использованием позднего связывания 573 Возможное применение на практике рефлексии, позднего связывания и специальных атрибутов 575 Создание расширяемого приложения 576 Создание сборки CommonSnappableTypes . dll 576 Создание оснастки на С# 577 Создание оснастки на Visual Basic 577 Создание расширяемого приложения Windows Fbrms 578 Резюме 581 Глава 16. Процессы, домены приложений и контексты объектов 582 Роль процесса Windows 582 Роль потоков 583 Взаимодействие с процессами в рамках платформы .NET 585 Перечисление выполняющихся процессов 587 Изучение конкретного процесса 588 Изучение набора потоков процесса 588 Изучение набора модулей процесса 590 Запуск и останов процессов программным образом 592 Управление запуском процесса с использованием класса ProcessStartlnfo 592 Домены приложений .NET 594 Класс System.AppDomain 594 Взаимодействие с используемым по умолчанию доменом приложения 596 Перечисление загружаемых сборок 597 Получение уведомлений о загрузке сборок 598 Создание новых доменов приложений 599 Загрузка сборок в специальные домены приложений 600 Выгрузка доменов приложений программным образом 601 Границы контекстов объектов 602 Контекстно-свободные и контекстно-зависимые типы 603 Определение контекстно-зависимого объекта 604 Инспектирование контекста объекта 604 Итоговые сведения о процессах, доменах приложений и контекстах 606 Резюме 606 Глава 17. Язык CIL и роль динамических сборок 607 Причины для изучения грамматики языка CIL 607 Директивы, атрибуты и коды операций в CIL 608 Роль директив CIL 609 Роль атрибутов CIL ; 609 Роль кодов операций CIL 609 Разница между кодами операций и их мнемоническими эквивалентами в CIL 609
Содержание 17 Помещение и извлечение данных из стека в CIL 610 Двунаправленное проектирование 612 Роль меток в коде CIL 615 Взаимодействие с CIL: модификация файла * . i 1 616 Компиляция CIL-кодас помощью ilasm.exe 617 Создание CIL-кода с помощью SharpDevelop 618 Рольpeverify.exe 619 Использование директив и атрибутов в CIL 619 Добавление ссылок на внешние сборки в CIL 619 Определение текущей сборки в CIL 620 Определение пространств имен в CIL 620 Определение типов классов в CIL 621 Определение и реализация интерфейсов в CIL 622 Определение структур в CIL 623 Определение перечислений в CIL 623 Определение обобщений в CIL 623 Компиляция файла CILTypes . il 624 Соответствия между типами данных в библиотеке базовых классов .NET, C# и CIL 625 Определение членов типов в CIL 626 Определение полей данных в CIL 626 Определение конструкторов для типов в CIL 627 Определение свойств в CIL 627 Определение параметров членов 628 Изучение кодов операций в CIL 628 Директива .maxstack 631 Объявление локальных переменных в CIL 631 Отображение параметров на локальные переменные в CIL 632 Скрытая ссылка this 633 Представление итерационных конструкций в CIL 633 Создание сборки . NET на CIL 634 Создание С ILCars.dll 634 СозданиеCILCarClient.exe 637 Динамические сборки 638 Пространство имен System. Re f lection. Emit 639 Роль типа System. Reflection. Emit. ILGenerator 640 Создание динамической сборки 641 Генерация сборки и набора модулей 642 Роль типа Module Builder 643 Генерация типа HelloClassn принадлежащей ему строковой переменной 644 Генерация конструкторов 645 Генерация метода S а у Н е 11 о () 646 Использование динамически сгенерированной сборки 646 Резюме 647 Глава 18. Динамические типы и исполняющая среда динамического языка 648 Роль ключевого слова С# dynamic 648 Вызов членов на динамически объявленных данных 650 Роль сборки Microsoft.CSharp.dll 651 Область применения ключевого слова dynamic 652 Ограничения ключевого слова dynamic 653 Практическое применение ключевого слова dynamic 653 Роль исполняющей среды динамического языка (DLR) 654 Роль деревьев выражений 654 Роль пространства имен System. Dynamic 655
18 Содержание Динамический поиск в деревьях выражений во время выполнения 655 Упрощение вызовов позднего связывания с использованием динамических типов 656 Использование ключевого слова dynamic для передачи аргументов 657 Упрощение взаимодействия с СОМ посредством динамических данных 659 Роль первичных сборок взаимодействия 660 Встраивание метаданных взаимодействия 661 Общие сложности взаимодействия с СОМ 661 Взаимодействие с СОМ с использованием средств языка С# 4.0 662 Взаимодействие с СОМ без использования средств языка С# 4.0 666 Резюме 667 Часть V. Введение в библиотеки базовых классов .NET 669 Глава 19. Многопоточность и параллельное программирование 670 Отношения между процессом, доменом приложения, контекстом и потоком 670 Проблема параллелизма 671 Роль синхронизации потоков 672 Краткий обзор делегатов .NET 672 Асинхронная природа делегатов 674 Методы BeginlnvokeO и EndlnvokeO ч 675 Интерфейс System.IAsyncResult 675 Асинхронный вызов метода 676 Синхронизация вызывающего потока 676 Роль делегата AsyncCallback 678 Роль класса AsyncResult 679 Передача и прием специальных данных состояния 680 Пространство имен System.Threading 681 Класс System.Threading.Thread 682 Получение статистики о текущем потоке 683 Свойство Name 684 Свойство Priority 684 Программное создание вторичных потоков 685 Работа с делегатом ThreadStart 685 Работа с делегатом ParametrizedThreadStart 687 Класс AutoResetEvent 688 Потоки переднего плана и фоновые потоки 689 Пример проблемы, связанной с параллелизмом 690 Синхронизация с использованием ключевого слова С# 1о с к 692 Синхронизация с использованием типа System.Threading.Monitor 694 Синхронизация с использованием типа System.Threading. Interlocked 695 Синхронизация с использованием атрибута [Synchronization] 696 Программирование с использованием обратных вызовов Timer 697 Пул потоков CLR 698 Параллельное программирование на платформе .NET 700 Интерфейс Tksk Parallel Library API 700 Роль класса Parallel 701 Понятие параллелизма данных 702 Класс Task 703 Обработка запроса на отмену 704 Понятие параллелизма задач 705 Запросы параллельного LINQ (PLINQ) 708 Выполнение запроса PLINQ 709 Отмена запроса PLINQ 709 Резюме 710
Содержание 19 Глава 20. Файловый ввод-вывод и сериализация объектов 711 Исследование пространства имен System.10 711 Классы Directory (Directorylnfo) и File (FileInfo) 712 Абстрактный базовый класс FileSystemlnfo 713 Работа с типом Directorylnfo 714 Перечисление файлов с помощью типа Directorylnfo 715 Создание подкаталогов с помощью типа Directorylnfo 716 Работа с типом Directory 717 Работа с типом Drive Info < 717 Работа с классом File Info 719 Метод Filelnfo.Create () 719 Метод FileJnfo.OpenO 720 Методы FileOpen.OpenRead() и Filelnfo.OpenWrite() 721 Метод Filelnfo.OpenText() 722 Методы Filelnfо.CreateTextO и Filelnfo.AppendTextO 722 Работа с типом File 722 Дополнительные члены File 723 Абстрактный класс Stream 724 Работа с классом FileStream 725 Работа с классами StreamWriter и StreamReader 726 Запись в текстовый файл 727 Чтение из текстового файла 728 Прямое создание экземпляров классов StreamWriter/StreamReader 729 Работа с классами StringWriter и StringReader 730 Работа с классами BinaryWriter и BinaryReader 731 Программное отслеживание файлов 732 Понятие сериализации объектов 734 Роль графов объектов 736 Конфигурирование объектов для сериализации 737 Определение сериализуемых типов 737 Общедоступные поля, приватные поля и общедоступные свойства 738 Выбор форматера сериализации 738 Интерфейсы IFormatter и IRemotingFormatter 739 Точность типов среди форматеров 740 Сериализация объектов с использованием BinaryFormatter 741 Десериализация объектов с использованием BinaryFormatter 742 Сериализация объектов с использованием SoapFormatter 743 Сериализация объектов с использованием XmlSerializer 743 Управление генерацией данных XML 744 Сериализация коллекций объектов 746 Настройка процессов сериализации SOAP и двоичной сериализации 747 Углубленный взгляд на сериализацию объектов 748 Настройка сериализации с использованием интерфейса ISerializable 749 Настройка сериализации с использованием атрибутов 751 Резюме 752 Глава 21. AD0.NET, часть I: подключенный уровень 754 Высокоуровневое определение ADO. NET 754 Три стороны ADO.NET 755 Поставщики данных ADO.NET 756 Поставщики данных ADO.NET от Microsoft 757 О сборке System.Data.OracleClient.dll 759 Получение сторонних поставщиков данных ADO.NET 759 Дополнительные пространства имен ADO.NET 759
20 Содержание Типы из пространства имен System.Data 760 Роль интерфейса IDbConпесtion 761 Роль интерфейса I DbTrans act ion 762 Роль интерфейса IDbCommand 762 Роль интерфейсов IDbDataParameter и IDataParameter 762 Роль интерфейсов IDbDataAdapter и IDataAdapter 763 Роль интерфейсов IDataReader и IDataRecord 763 Абстрагирование поставщиков данных с помощью интерфейсов 764 Повышение гибкости с помощью конфигурационных файлов приложения 766 Создание базы данных AutoLot 767 Создание таблицы Inventory 767 Создание хранимой процедуры GetPetName () 769 Создание таблиц Customers и Orders 770 Визуальное создание отношений между таблицами 772 Модель генератора поставщиков данных ADO. NET 111 Полный пример генератора поставщиков данных 774 Возможные трудности с моделью генератора поставщиков 776 Элемент<connectionStrings> 777 Подключенный уровень ADO.NET 778 Работа с объектами подключения 779 Работа с объектами ConnectionStringBuilder 781 Работа с объектами команд 782 Работа с объектами чтения данных 783 Получение множественных результатов с помощью объекта чтения данных 784 Создание повторно используемой библиотеки доступа к данным 785 Добавление логики подключения 786 Добавление логики вставки 787 Добавление логики удаления 788 Добавление логики изменения 788 Добавление логики выборки 789 Работа с параметризованными объектами команд 790 Выполнение хранимой процедуры 792 Создание консольного пользовательского интерфейса 793 Реализация метода М a i n () 794 Реализация метода Showlnstructions () 795 Реализация метода List Inventory () 795 Реализация метода DeleteCarO 796 Реализация метода InsertNewCar () 797 Реализация метода UpdateCarPetName () 797 Реализация метода LookUpPetName () 798 Транзакции баз данных 799 Основные члены объекта транзакции ADO.NET 800 Добавление таблицы CreditRisksB базу данных AutoLot 800 Добавление метода транзакции в InventoryDAL 801 Тестирование транзакции в нашей базе данных 802 Резюме 803 Глава 22. AD0.NET, часть II: автономный уровень 804 Знакомство с автономным уровнем ADO. NET 804 Роль объектов Data Set 805 Основные свойства класса Data Set 806 Основные методы класса Data Set 807 Создание DataSet 807 Работа с объектами DataColumn 808
Содержание 21 Создание объекта DataColumn 809 Включение автоинкрементных полей 810 Добавление объектов DataColumn в DataTable 810 Работа с объектами DataRow 810 Свойство RowState 812 Свойство DataRowVersion 813 Работа с объектами DataTable 814 Вставка объектов DataTable в DataSet 815 Получение данных из объекта DataSet 815 Обработка данных из DataTable с помощью объектов DataTableReader 816 Сериализация объектов DataTable и DataSet в формате XML 817 Сериализация объектов DataTable и DataSet в двоичном формате 818 Привязка объектов DataTable к графическим интерфейсам Windows Forms 819 Заполнение DataTable из обобщенного List<T> 820 Удаление строк из DataTable 822 Выборка строк с помощью фильтра 823 Изменение строк в DataTable 826 Работа с типом DataView 826 Работа с адаптерами данных 828 Простой пример адаптера данных 829 Замена имен из базы данных более понятными названиями 829 Добавление в AutoLotDAL. dl 1 возможности отключения 830 Определение начального класса 831 Настройка адаптера данных с помощью SqlCommandBuilder 831 Реализация метода GetAlllnventory () 833 Реализация метода Updatelnventory () 833 Установка номера версии 833 Тестирование автономной функциональности 833 Объекты DataSet для нескольких таблиц и взаимосвязь данных 834 Подготовка адаптеров данных 835 Создание отношений между таблицами 836 Изменение таблиц базы данных 837 Переходы между взаимосвязанными таблицами 837 Средства конструктора баз данных в Windows Forms 839 Визуальное проектирование элементов DataGridView 840 Сгенерированный файл арр. conf ig 843 Анализ строго типизированного DataSet 843 Анализ строго типизированного DataTable 845 Анализ строго типизированного DataRow 845 Анализ строго типизированного адаптера данных 845 Завершение приложения Windows Forms 846 Выделение строго типизированного кода работы с базами данных в библиотеку классов 847 Просмотр сгенерированного кода 848 Выборка данных с помощью сгенерированного кода 849 Вставка данных с помощью сгенерированного кода 850 Удаление данных с помощью сгенерированного кода 850 Вызов хранимой процедуры с помощью сгенерированного кода 851 Программирование с помощью LINQ to DataSet 851 Библиотека расширений DataSet 853 Получение DataTable, совместимого с LINQ 853 Метод расширения DataRowExtensions.Field<T>() 855 Заполнение новых объектов DataTable с помощью LINQ-запросов 855 Резюме 856
22 Содержание Глава 23. ADO.NET, часть III: Entity Framework 857 Роль Entity Framework 857 Роль сущностей 859 Строительные блоки Entity Framework 860 Роль служб объектов 861 Роль клиента сущности 861 Роль файла *.edmx 863 Роль классов ObjectContext и ObjectSet<T> 863 Собираем все вместе 865 Построение и анализ первой модели EDM 866 Генерация файла *. е dmx 866 Изменение формы сущностных данных 869 Просмотр отображений 871 Просмотр данных сгенерированного файла *. еdmx 871 Просмотр сгенерированного исходного кода 873 Улучшение сгенерированного исходного кода 875 Программирование с использованием концептуальной модели 875 Удаление записи 876 Обновление записи 877 Запросы с помощью LINQ to Entities 878 Запросы с помощью Entity SQL 879 Работа с объектом EntityDataReader 880 Проект AutoLotDAL версии 4.0, теперь с сущностями 881 Отображение хранимой процедуры 881 Роль навигационных свойств 882 Использование навигационных свойств внутри запросов LINQ to Entity 884 Вызов хранимой процедуры 885 Привязка данных сущностей к графическим пользовательским интерфейсам Windows Fbrms 886 Добавление кода привязки данных 888 Резюме 890 Глава 24. Введение в LINQ to XML 891 История о двух API-интерфейсах XML 891 Интерфейс LINQ to XML как лучшая модель DOM 893 Синтаксис литералов Visual Basic как наилучший интерфейс LINQ to XML 893 Члены пространства имен System.Xml.Linq 895 Осевые методы LINQ to XML 895 Избыточность XName (и XNamespace) 897 Работа с XElement HXDocument 898 Генерация документов из массивов и контейнеров 899 Загрузка и разбор XML-содержимого 901 Манипулирование XML-документом в памяти 901 Построение пользовательского интерфейса приложения LINQ to XML 901 Импорт файла Inventory, xml 902 Определение вспомогательного класса LINQ to XML 902 Оснащение пользовательского интерфейса вспомогательными методами 904 Резюме 905 Глава 25. Введение в Windows Communication Foundation 906 API-интерфейсы распределенных вычислений 906 Роль DCOM 907 Роль служб СОМ+/Enterprise Services 908 Роль MSMQ 909
Содержание 23 Роль .NET Remotlng 909 Роль веб-служб XML 910 Именованные каналы, сокеты и Р2Р 913 Роль^УСК 913 Обзор средств WCF 914 Обзор архитектуры, ориентированной на службы 914 WCF: итоги 915 Исследование основных сборок WCF 916 Шаблоны проектов WCF в Visual Studio 917 Шаблон проекта WCF Service 918 Базовая композиция приложения WCF 919 Понятие ABC в WCF 920 Понятие контрактов WCF 920 Понятие привязок WCF 921 Понятие адресов WCF 924 Построение службы WCF 925 Атрибут [ServiceContract] 926 Атрибут [OperationContract] 927 Служебные типы как контракты операций 928 Хостинг службы WCF 928 УстановкаАВСвнутрифайлаАрр.соп:£1д 929 Кодирование с использованием типа ServiceHost 930 Указание базового адреса 930 Подробный анализ типа ServiceHost 932 Подробный анализ элемента <system.serviceModel> 933 Включение обмена метаданными 934 Построение клиентского приложения WCF 936 Генерация кода прокси с использованием svcutil.exe 937 Генерация кода прокси с использованием Visual Studio 2010 938 Конфигурирование привязки на основе TCP 939 Упрощение конфигурационных настроек в WCF 4.0 940 Конечные точки по умолчанию в WCF 4.0 941 Предоставление одной службы WCF с использованием множества привязок 942 Изменение установок для привязки WCF 943 Конфигурация поведения МЕХ по умолчанию в WCF 4.0 944 Обновление клиентского прокси и выбор привязки 945 Использование шаблона проекта WCF Service Library 946 Построение простой математической службы 947 Тестирование службы WCFс помощью WcfTestClient.exe 947 Изменение конфигурационных файлов с помощью SvcConfigEditor.exe 948 Хостинг службы WCF в виде службы Windows 949 Спецификация ABC в коде 950 Включение МЕХ 951 Создание программы установки для службы Windows 952 Установка службы Windows 953 Асинхронный вызов службы на стороне клиента 954 Проектирование контрактов данных WCF 955 Использование веб-ориентированного шаблона проекта WCF Service 956 Реализация контракта службы 958 Роль файла *.svc 959 Содержимое файла Web. с on fig 959 Тестирование службы 960 Резюме 960
24 Содержание Глава 26. Введение в Windows Workflow Foundation 4.0 961 Определение бизнес-процесса 962 Роль WF 4.0 962 Построение простого рабочего потока 963 Просмотр полученного кода XAML 965 Исполняющая среда WF 4.0 967 Хостинг рабочего потока с использованием класса Workf lowlnvoker 967 Хостинг рабочего потока с использованием класса Workf lowApplication 970 Переделка первого рабочего потока 971 Знакомство с действиями Windows Workflow 4.0 971 Действия потока управления 971 Действия блок-схемы 972 Действия обмена сообщениями 973 Действия исполняющей среды и действия-примитивы 973 Действия транзакций 974 Действия над коллекциями и действия обработки ошибок 974 Построение рабочего потока в виде блок-схемы 975 Подключение действий к блок-схеме 975 Работа с действием InvokeMethod 976 Определение переменных уровня рабочего потока 977 Работа с действием FlowDecision 978 Работа с действием TerminateWorkf low 978 Построение условия "true" 979 Работа с действием ForEach<T> 979 Завершение приложения 981 Промежуточные итоги 982 Изоляция рабочих потоков в выделенных библиотеках 984 Определение начального проекта 984 Импорт сборок и пространств имен 985 Определение аргументов рабочего потока 986 Определение переменных рабочего потока 986 Работа с действием Assign 987 Работа с действиями IfnSwitch 987 Построение специального действия кода 988 Использование библиотеки рабочего потока 991 Получение выходного аргумента рабочего потока 992 Резюме 993 Часть VI. Построение настольных пользовательских приложений с помощью WPF 995 Глава 27. Введение в Windows Presentation Foundation и XAML 996 Мотивация, лежащая в основе WPF 997 Унификация различных API-интерфейсов 997 Обеспечение разделения ответственности через XAML 998 Обеспечение оптимизированной модели визуализации 998 Упрощение программирования сложных пользовательских интерфейсов 999 Различные варианты приложений WPF 1000 Традиционные настольные приложения 1000 WPF-приложения на основе навигации 1001 Приложения ХВАР 1002 Отношения между WPF и Silverlight 1003 Исследование сборок WPF 1004
Содержание 25 Роль класса Application 1005 Роль класса Wi ndow 1007 Роль класса System.Windows.Controls.ContentControl 1007 Роль класса System.Windows.Controls.Control 1008 Роль класса System.Windows.FrameworkElement 1009 Роль класса System.Windows.UIElement 1010 Роль класса System.Windows.Media.Visual 1010 Роль класса System. Windows. DependencyObject 1010 Роль класса System.Windows.Threading.DispatcherObject 1011 Построение приложения WPF без XAML 1011 Создание строго типизированного окна 1013 Создание простого пользовательского интерфейса 1013 Взаимодействие с данными уровня приложения 1015 Обработка закрытия объекта Window 1016 Перехват событий мыши 1017 Перехват клавиатурных событий 1018 Построение приложения WPF с использованием только XAML 1019 Определение Ma inWindow в XAML 1020 Определение объекта Application в XAML 1021 Обработка файлов XAML с помощью msbuild.exe 1022 Трансформация разметки в сборку .NET 1023 Отображение XAML-данных окна на код С# 1024 PoльBAML 1025 Отображение XAML-данных приложения на код С# 1026 Итоговые замечания о процессе трансформирования XAML в сборку 1027 Синтаксис XAML для WPF 1027 Введение в Kaxaml 1027 Пространства имен XAML XML и "ключевые слова" XAML 1029 Управление объявлениями классов и переменных-членов 1031 Элементы XAML, атрибуты XAML и преобразователи типов 1032 Понятие синтаксиса XAML "свойство-элемент" 1033 Понятие присоединяемых свойств XAML 1034 Понятие расширений разметки XAML 1034 Построение приложений WPF с использованием файлов отделенного кода 1036 Добавление файла кода для класса MainWindow 1036 Добавление файла кода для класса МуАрр 1037 Обработка файлов кода с помощью msbuild.exe 1038 Построение приложений WPF с использованием Visual Studio 2010 1038 Шаблоны проектов WPF 1039 Знакомство с инструментами визуального конструктора WPF 1039 Проектирование графического интерфейса окна 1042 Реализация события Loaded 1044 Реализация события Click объекта Button 1045 Реализация события Closed 1046 Тестирование приложения 1046 Резюме 1047 Глава 28. Программирование с использованием элементов управления WPF 1048 Обзор библиотеки элементов управления WPF 1048 Работа с элементами управления WPF в Visual Studio 2010 1049 Элементы управления Ink API 1051 Элементы управления документами WPF 1051 Общие диалоговые окна WPF 1052 Подробные сведения находятся в документации 1052
26 Содержание Управление компоновкой содержимого с использованием панелей 1053 Позиционирование содержимого внутри панелей Canvas 1054 Позиционирование содержимого внутри панелей WrapPanel 1056 Позиционирование содержимого внутри панелей StackPanel 1058 Позиционирование содержимого внутри панелей Grid 1059 Позиционирование содержимого внутри панелей DockPanel 1060 Включение прокрутки в типах панелей 1061 Построение главного окна с использованием вложенных панелей 1062 Построение системы меню 1063 Построение панели инструментов 1064 Построение строки состояния 1065 Завершение дизайна пользовательского интерфейса 1065 Реализация обработчиков событий MouseEnter/MouseLeave 1066 Реализация логики проверки правописания 1066 Понятие управляющих команд WPF 1067 Внутренние объекты управляющих команд 1067 Подключение команд к свойству Command 1068 Подключение команд к произвольным действиям 1069 Работа с командами Open и Save 1071 Построение пользовательского интерфейса WPF с помощью Expression Blend 1072 Ключевые аспекты IDE-среды Expression Blend 1073 Использование элемента TabControl 1077 Построение вкладки Ink API 1079 Проектирование элемента Tool Bar 1080 Элемент управления RadioButton 1082 Элемент управления InkCanvas 1084 Элемент управления С оmboB ox 1086 Сохранение, загрузка и очистка данных InkCanvas 1087 Введение в интерфейс Documents API 1088 Блочные элементы и встроенные элементы 1088 Диспетчеры компоновки документа 1089 Построение вкладки Documents 1089 Наполнение FlowDocument с использованием Blend 1091 Наполнение FlowDocument с помощью кода 1091 Включение аннотаций и "клейких" заметок 1093 Сохранение и загрузка потокового документа 1094 Введение в модель привязки данных WPF 1095 Построение вкладки Data Binding 1096 Установка привязки данных с использованием Blend 1096 Свойство DataContext 1097 Преобразование данных с использованием IValueConverter 1099 Установка привязок данных в коде 1099 Построение вкладки DataGrid 1100 Резюме 1102 Глава 29. Службы визуализации графики WPF 1103 Службы графической визуализации WPF 1103 Опции графической визуализации WPF 1104 Визуализация графических данных с использованием фигур 1105 Добавление прямоугольников, эллипсов и линий на поверхность Canvas 1107 Удаление прямоугольников, эллипсов и линий с поверхности Canvas 1110 Работа с элементами Polyline и Polygon 1110 Работа с элементом Path 1111 Кисти и перья WPF 1115
Содержание 27 Конфигурирование кистей с использованием Visual Studio 2010 1115 Конфигурирование кистей в коде 1117 Конфигурирование перьев 1118 Применение графических трансформаций 1118 Первый взгляд на трансформации 1119 Трансформация данных Canvas 1120 Работа с фигурами в Expression Blend 1122 Выбор фигуры для визуализации из палитры инструментов 1122 Преобразование фигур в пути 1123 Комбинирование фигур 1123 Редакторы кистей и трансформаций 1123 Визуализация графических данных с использованием рисунков и геометрий 1125 Построение кисти DrawingBrush с использованием объектов Geometry 1126 Рисование с помощью DrawingBrush 1127 Включение типов Drawing в Drawinglmage 1128 Генерация сложной векторной графики с использованием Expression Design 1128 Экспорт документа Expression Design в XAML 1129 Визуализация графических данных с использованием визуального уровня 1130 Базовый класс Vi s u a 1 и производные дочерние классы 1130 Первый взгляд на класс DrawingVisual 1131 Визуализация графических данных в специальном диспетчере компоновки 1133 Реагирование на операции проверки попадания 1134 Резюме 1136 Глава 30. Ресурсы, анимация и стили WPF 1137 Система ресурсов WPF 1137 Работа с двоичными ресурсами 1138 Программная загрузка изображения 1139 Работа с объектными (логическими) ресурсами 1142 Роль свойства Resources 1143 Определение ресурсов уровня окна 1143 Расширение разметки {StaticResource} 1145 Изменение ресурса после извлечения 1145 Расширение разметки {DynamicResource} 1146 Ресурсы уровня приложения 1146 Определение объединенных словарей ресурсов 1147 Определение сборки из одних ресурсов 1149 Извлечение ресурсов в Expression Blend 1150 Службы анимации WPF 1152 Роль классов анимации 1152 Свойства То, From и By 1153 Роль базового класса Timeline 1154 Написание анимации в коде С# 1154 Управление темпом анимации 1155 Запуск в обратном порядке и циклическое выполнение анимации 1156 Описание анимации в XAML 1157 Роль раскадровки 1158 Роль триггеров событий 1158 Анимация с использованием дискретных ключевых кадров s 1159 Роль стилей WPF 1160 Определение и применение стиля 1160 Переопределение настроек стиля 1161 Автоматическое применение стиля с помощью TargetType 1161 Создание подклассов существующих стилей 1162
28 Содержание Роль безымянных стилей 1163 Определение стилей с триггерами 1164 Определение стилей с множеством триггеров 1164 Анимированные стили 1165 Программное применение стилей 1165 Генерация стилей с помощью Expression Blend 1166 Работа с визуальными стилями по умолчанию 1167 Резюме 1169 Глава 31. Шаблоны элементов управления WPF и пользовательские элементы управления 1170 Роль свойств зависимости 1170 Проверка существующего свойства зависимости 1172 Важные замечания относительно оболочек свойств CLR 1175 Построение специального свойства зависимости 1176 Добавление процедуры проверки достоверности данных 1179 Реакция на изменение свойства 1179 Маршрутизируемые события 1181 Роль маршрутизируемых пузырьковых событий 1182 Продолжение или прекращение пузырькового распространения 1182 Роль маршрутизируемых туннелируемых событий 1183 Логические деревья, визуальные деревья и шаблоны по умолчанию 1185 Программный просмотр логического дерева 1185 Программный просмотр визуального дерева 1187 Программный просмотр шаблона по умолчанию для элемента управления 1188 Построение специального шаблона элемента управления в Visual Studio 2010 1191 Шаблоны как ресурсы 1192 Включение визуальных подсказок с использованием триггеров 1193 Роль расширения разметки {TemplateBinding} 1194 Роль класса ContentPresenter 1196 Включение шаблонов в стили 1196 Построение специальных элементов UserControl с помощью Expression Blend 1197 Создание проекта библиотеки UserControl 1198 Создание WPF-приложения JackpotDeluxe 1204 Извлечение UserControl из геометрических объектов 1204 Роль визуальных состояний .NET 4.0 1205 Завершение приложения JackpotDeluxe 1209 Резюме 1212 Часть VII. Построение веб-приложений с использованием ASP.NET 1213 Глава 32. Построение веб-страниц ASP.NET 1214 Роль протокола HTTP 1214 Цикл запрос / ответ HTTP 1215 HTTP — протокол без поддержки состояния 1215 Веб-приложения и веб-серверы 1216 Роль виртуальных каталогов IIS 1216 Веб-сервер разработки ASP NET 1217 Роль языка HTML 1217 Структура HTML-документа 1218 Роль формы HTML 1219 Инструменты визуального конструктора HTML в Visual Studio 2010 1219 Построение формы HTML 1220 Роль сценариев клиентской стороны 1221 Пример сценария клиентской стороны 1223
Содержание 29 Обратная отправка веб-серверу 1224 Обратные отправки в ASP.NET 1225 Набор средств API-интерфейса ASP NET 1225 Основные средства ASP.NET 1.0-1.1 1225 Основные средства ASP.NET 2.0 1227 Основные средства ASP NET 3.5 (и .NET 3.5 SP1) 1228 Основные средства ASP.NET 4.0 1228 Построение однофайловой веб-страницы ASP.NET 1229 Ссылка на сборку AutoLotDAL.dll 1229 Проектирование пользовательского интерфейса 1230 Добавление логики доступа к данным 1231 Роль директив ASP NET 1233 Анализ блока script 1235 Анализ объявлений элементов управления ASP NET 1235 Цикл компиляции для однофайловых страниц 1236 Построение веб-страницы ASP.NET с использованием файлов кода 1237 ^Ссылка на сборку AutoLotDAL.dll 1239 Обновление файла кода 1240 Цикл компиляции многофайловых страниц 1240 Отладка и трассировка страниц ASP NET 1241 Веб-сайты и веб-приложения ASP.NET 1242 Структура каталогов веб-сайта ASP.NET 1243 Ссылаемые сборки 1244 Роль папки App_Code 1244 Цепочка наследования типа Page 1245 Взаимодействие с входящим запросом HTTP 1246 Получение статистики браузера 1247 Доступ к входным данным формы 1248 Свойство IsPostBack 1248 Взаимодействие с исходящим ответом HTTP 1249 Выдача HTML-содержимого 1250 Перенаправление пользователей 1250 Жизненный цикл веб-страницы ASP.NET 1251 Роль атрибута AutoEventWireup 1252 Событие Error 1253 Роль файла Web. con fig 1254 Утилита администрирования веб-сайтов ASP.NET 1255 Резюме 1256 Глава 33. Веб-элементы управления, мастер-страницы и темы ASP.NET 1257 Природа веб-элементов управления 1257 Обработка событий серверной стороны 1258 Свойство AutoPostBack 1259 Базовые классы Control и WebControl 1260 Перечисление содержащихся элементов управления 1260 Динамическое добавление и удаление элементов управления 1262 Взаимодействие с динамически созданными элементами управления 1263 Функциональность базового класса WebControl 1264 Основные категории веб-элементов управления ASP NET 1265 Краткая информация о System.Web.UI.HtmlControls 1266 Документация по веб-элементам управления 1267 Построение веб-сайта ASPNET Cars e 1268 Работа с мастер-страницами 1268 Определение страницы содержимого Default.aspx 1274
30 Содержание Проектирование страницы содержимого Inventory, aspx 1276 Проектирование страницы содержимого BuildCar. aspx 1279 Роль элементов управления проверкой достоверности 1282 Класс RequiredFieldValidator 1283 Класс RegularExpressionValidator 1284 Класс RangeValidator 1284 Класс CompareValidator 1284 Создание итоговой панели проверки достоверности 1285 Определение групп проверки достоверности 1286 Работа с темами 1288 Файлы*, skin 1289 Применение тем ко всему сайту 1291 Применение тем на уровне страницы 1291 Свойство SkinID 1291 Программное назначение тем 1292 Резюме 1293 Глава 34. Управление состоянием в ASP.NET 1294 Проблема поддержки состояния 1294 Приемы управления состоянием ASP. NET 1296 Роль состояния представления ASP NET 1296 Демонстрация работы с состоянием представления 1297 Добавление специальных данных в состояние представления 1299 Роль файла Global.asax 1299 Глобальный обработчик исключений "последнего шанса" 1301 Базовый класс HttpApplication 1302 Различие между свойствами Application и Session 1303 Поддержка данных состояния уровня приложения 1303 Модификация данных приложения 1305 Обработка останова веб-приложения 1306 Работа с кэшем приложения 1307 Работа с кэшированием данных 1307 Модификация файла *. a sрх 1309 Поддержка данных сеанса 1311 Дополнительные члены HttpSessionState 1314 Cookie-наборы 1315 Создание cookie-наборов 1315 Чтение входящих cookie-данных 1316 Роль элемента <sessionState> 1317 Хранение данных сеанса на сервере состояния сеансов ASP NET 1317 Хранение информации о сеансах в выделенной базе данных 1318 Интерфейс ASPNET Profile API 1319 База данных ASPNETDB.mdf 1319 Определение пользовательского профиля BWeb.config 1320 Программный доступ к данным профиля 1322 Группирование данных профиля и сохранение специальных объектов 1323 Резюме 1325 Часть VIII. Приложения 1327 Приложение А. Программирование с помощью Windows Forms 1328 Приложение Б. Независимая от платформы разработка .NET-приложений с помощью Mono 1369 Предметный указатель 1386
Об авторе Эндрю Троелсен (Andrew Trolesen) с любовью вспоминает свой самый первый компьютер Atari 400, оснащенный кассетным устройством хранения и черно-белым телевизионным монитором "(который его родители разрешили ему поставить у себя в спальне, за что им большое спасибо). Еще он благодарен ушедшему в небытие журналу Compute!, степени бакалавра в области математической лингвистики и трем годам формального изучения санскрита. Все это оказало значительное влияние на его сегодняшнюю карьеру. В настоящее время Эндрю работает в центре Intertech, занимающемся консультированием и обучением работе с .NET и Java (www .intertech. com). На его счету уже несколько написанных книг, в числе которых Developer's Workshop to COM and ATL 3.0 {Wordware Publishing', 2000 г.), COMand.NET'Interoperability (Apress, 2002 r.) и Visual Basic 2008 and the .NET 3.5 Platform: An Advanced Guide (Apress, 2008 г.). 0 техническом редакторе Энди Олсен (Andy Olsen) — независимый консультант и инструктор, проживающий в Великобритании. Энди работает с .NET, начиная с самой первой бета-версии этого продукта, и занимается активным исследованием новых функциональных возможностей, которые появились в .NET 4.O. Он живет у моря в городе Суонси вместе со своей женой Джейн и детьми Эмили и Томом. Любит делать пробежки вдоль побережья (регулярно останавливаясь на чашечку кофе по пути), кататься на лыжах и следить за лебедями и орликами. Связаться с ним можно по адресу andyo@olsensof t. com. Благодарности По какой-то непонятной причине (а может и целому ряду причин) настоящее издание книги оказалось гораздо более трудным в написании, чем ожидалось. Если бы не помощь и поддержка многих хороших людей, скорее всего, оно не вышло бы в свет так скоро. Прежде всего, огромное спасибо техническому редактору Энди Олсену. Помимо указания на пропущенные точки с запятой он привнес массу замечательных предложений, позволивших сделать первоначальные примеры кода более понятными и точными. Спасибо тебе, Энди! Далее хочу выразить благодарность всей команде литературных редакторов, а именно — Мэри Бер (Магу Вепг), Патрику Мидеру (Patrick Meader), Кэти Стэнс (Katie Stence) и Шэрон Тердеман (Sharon Terdeman), которые выявили и устранили массу грамматических ошибок. Особая благодарность Дебре Кэлли (Debra Kelly) из Apress. Это наш первый совместный проект и, несмотря на многочисленные опоздания с предоставлением глав, путаницу с электронными письмами и постоянное добавление обновлений, я все равно очень надеюсь, что она согласится работать со мной снова. Спасибо тебе, Дебра! И, наконец, напоследок спасибо моей жене Мэнди. Как всегда, ты поддерживаешь меня в здравом уме во время написания всех моих проектов.
Введение Первое издание настоящей книги появилось вместе с выпуском Microsoft второй бета-версии .NET 1.0 (летом 2001 г.) и с тех пор постоянно переиздавалось в том или ином виде. С того самого времени автор с чрезвычайным удовольствием и благодарностью наблюдал за тем, как данная работа продолжала пользоваться популярностью в прессе и, самое главное, среди читателей. Через некоторое время, в 2002 г., она даже была номинирована на премию Jolt Award (которую, увы, получить так и не удалось, но книга попала в число финалистов), а в 2003 г. — еще и на премию Referenceware Excellence Award, где стала лучшей книгой года по программированию. Даже еще более важно то, что автору стали приходить электронные письма от читателей со всего мира. Общаться с множеством людей и узнавать, что данная книга как-то помогла им в карьере, просто замечательно. В связи с этим, хотелось бы отметить, что настоящая книга с каждым разом становится все лучше именно благодаря читателям, которые присылают различные предложения по улучшению, указывают на допущенные в тексте опечатки и обращают внимание на прочие промахи. Автор был просто ошеломлен, узнав, что эта книга использовалась и продолжает использоваться на занятиях в колледжах и университетах и является обязательной для прочтения на многих предвыпускных и выпускных курсах в области вычислительной техники. Автор благодарит прессу, читателей, преподавателей и всех остальных и желает им успешного программирования! С самого первого выпуска настоящей книги автор прилагал все усилия и обновлял книгу так, чтобы она отражала текущие возможности каждой выходившей версии платформы .NET. В настоящем издании материал был полностью пересмотрен и расширен с целью охвата новых средств языка С# 2010 и платформы .NET 4.O. Вы найдете информацию по таким новым компонентам, как Dynamic Language Runtime (DLR), Task Parallel Library (TPL), Parallel LINQ (PLINQ) и ADO.NET Entity Framework (EF). Кроме того, в книге описан ряд менее значительных (но очень полезных) обновлений, наподобие именованных и необязательных аргументов в С# 2010, типа класса Lazy<T> и т.д. Помимо описания новых компонентов и возможностей, в книге по-прежнему предоставляется весь необходимый базовый материал по языку С# в целом, основам объектно-ориентированного программирования (ООП), конфигурированию сборок, получению доступа к базам данных (через ADO.NET), а также процессу построения настольных приложений с графическим пользовательским интерфейсом, веб-приложений и распределенных систем (и многим другим темам). Как и в предыдущих изданиях, в этом издании весь материал по языку программирования С# и библиотекам базовых классов .NET подается в дружественной и понятной читателю манере. Автор никогда не понимал, зачем другие технические авторы стараются писать свои книги так, чтобы те больше напоминали сложный научный труд, а не легкое для восприятия пособие. В новом издании основное внимание уделяется предоставлению информации, которой необходимо владеть для того, чтобы разрабатывать программные решения прямо сегодня, а не глубокому изучению малоинтересных эзотерических деталей.
Введение 33 Автор и читатели - одна команда Авторам книг по технологиям приходится писать для очень требовательной группы людей. Всем известно, что детали разработки программных решений с помощью любой платформы (.NET, Java и СОМ) очень сложны и сильно зависят от отдела, компании, клиентской базы и поставленной задачи. Кто-то работает в сфере электронных публикаций, кто-то занимается разработкой систем для правительства и региональных органов власти, а кто-то сотрудничает с НАСА или военными департаментами. Что касается автора настоящей книги, то сам он занимается разработкой детского образовательного ПО (возможно, вам приходилось слышать об Oregon Trail или Amazon Trail), а также различных многоуровневых систем и проектов в медицинской и финансовой сфере. А это значит, что код, который придется писать читателю, скорее всего, будет иметь мало чего общего с тем, с которым приходится иметь дело автору. Поэтому в настоящей книге автор специально старался избегать приведения примеров, свойственных только какой-то конкретной области производства или программирования. Из-за этого все концепции, связанные с С#, ООП, CLR и библиотеками базовых классов .NET, объясняются на общих примерах. В частности, здесь везде применяется одна и та же тема, которая так или иначе близка каждому — автомобили. Остальное остается за читателем. Задача автора состоит в том, чтобы максимально доступно объяснить читателю язык программирования С# и основные концепции платформы .NET настолько хорошо, а также описать все возможные инструменты и стратегии, которые могут потребоваться для продолжения обучения по прочтении данной книги. Задача читателя состоит в том, чтобы усвоить всю эту информацию и научиться применять ее на практике при разработке своих программных решений. Конечно, скорее всего, проекты, которые понадобится выполнять в будущем, не будут связаны с автомобилями и их дружественными именами, но именно в этом и состоит вся суть применения получаемых знаний на практике! После изучения представленных в этой книге концепций можно будет спокойно создавать решения .NET, удовлетворяющие требованиям любой среды программирования. Краткий обзор содержания Эта книга логически разделена на восемь частей, в каждой из которых содержится ряд взаимосвязанных между собой глав. Те, кто читал предыдущие издания данной книги, сразу же отметят ряд отличий. Например, новые средства языка С# больше не описываются в отдельной главе. Вместо этого они рассматриваются в тех главах, в которых их появление является вполне естественным. Кроме того, по просьбе читателей был значительно расширен материал по технологии Windows Presentation Foundation (WPF). Ниже приведено краткое описание содержимого каждой из частей и глав настоящей книги. Часть I. Общие сведения о языке С# и платформе .NET Назначением первой части этой книги является общее ознакомление читателя с природой платформы .NET и различными средствами разработки, которые могут применяться при построении приложений .NET (многие из которых распространяются с открытым исходным кодом), а также некоторыми основными концепциями языка программирования С# и системы типов .NET.
34 Введение Глава 1. Философия .NET Первая глава выступает в роли своего рода основы для изучения всего остального излагаемого в данной книге материала. В начале в ней рассказывается о традиционной разработке приложений Windows и недостатках, которые существовали в этой сфере ранее. Главной целью данной главы является ознакомление читателя с набором ключевых составляющих .NET: общеязыковой исполняющей средой (Common Language Runtime — CLR), общей системой типов (Common Type System — CTS), общеязыковой спецификацией (Common Language Specification — CLRS) и библиотеками базовых классов. Здесь читатель сможет получить первоначальное впечатление о том, что собой представляет язык программирования С#, и том, как выглядит формат сборок .NET, а также узнать о независимой от платформы природе .NET (о которой более' детально рассказывается в приложении Б). Глава 2. Создание приложений на языке С# Целью этой главы является ознакомление читателя с процессом компиляции файлов исходного кода на С# с применением различных средств и методик. Будет показано, как использовать компилятор командной строки С# (csc.exe) и файлы ответов, а также различные редакторы кода и интегрированные среды разработки, в том числе Notepad++, SharpDevelop, Visual C# 2010 Express и Visual Studio 2010. Кроме того, вы узнаете о том, как устанавливать на машину разработки локальную копию документации .NET Framework 4.0 SDK. Часть II. Главные конструкции программирования на С# Темы, представленные в этой части книги, довольно важны, поскольку подходят для разработки приложений .NET любого типа (т.е. веб-приложений, настольных приложений с графическим пользовательским интерфейсом, библиотек кода и служб Windows). Здесь читатель ознакомится с основными конструкциями языка С# и некоторыми деталями объектно-ориентированного программирования (ООП), обработкой исключений на этапе выполнения, а также автоматическим процессом сборки мусора в .NET. Глава 3. Главные конструкции программирования на С#; часть I В этой главе начинается формальное изучение языка программирования С#. Здесь читатель узнает о роли метода Main () и многочисленных деталях работы с внутренними типами данных в .NET, в том числе — о манипуляциях текстовыми данными с помощью System. String и System. Text. StringBuilder. Кроме того, будут описаны итерационные конструкции и конструкции принятия решений, операции сужения и расширения, а также ключевое слово unchecked. Глава 4. Главные конструкции программирования на С#; часть II В этой главе завершается рассмотрение ключевых аспектов С#. Будет показано, как создавать перегруженные методы в типах и определять параметры с использованием ключевых слов out, ref и par am s. Также рассматриваются появившиеся в С# 2010 концепции именованных аргументов и необязательных параметры. Кроме того, будут описаны создание и манипулирование массивами данных, определение нулевых типов (с помощью операций ? и ??) и отличия типов значения (включающих перечисления и специальные структуры) от ссылочных типов. Глава 5. Определение инкапсулированных типов классов В этой главе начинается рассмотрение концепций объектно-ориентированного программирования (ООП) в языке С#. Вначале объясняются базовые понятия ООП (та-
Введение 35 кие как инкапсуляция, наследование и полиморфизм). Затем показано, как создавать надежные типы классов с применением конструкторов, свойств, статических членов, констант и доступных только для чтения полей. Наконец, рассматриваются частичные определения типов, синтаксис инициализации объектов и автоматические свойства. Глава 6. Понятия наследования и полиморфизма Здесь читатель сможет ознакомиться с двумя такими основополагающими концепциями ООП, как наследование и полиморфизм, которые позволяют создавать семейства взаимосвязанных типов классов. Будет описана роль виртуальных и абстрактных методов (а также абстрактных базовых классов) и полиморфных интерфейсов. И, наконец, в главе рассматривается роль одного из главных базовых классов в .NET — System.Object. Глава 7. Структурированная обработка исключений В этой главе рассматривается решение проблемы аномалий, возникающих в коде во время выполнения, за счет применения методики структурированной обработки исключений. Здесь описаны ключевые слова, предусмотренные для этого в С# (try, catch, throw и finally), а также отличия между исключениями уровня приложения и уровня системы. Кроме того, рассматриваются различные инструменты, предлагаемые в Visual Studio 2010, которые предназначены для проведения отладки исключений. Глава 8. Время жизни объектов В этой главе рассказывается об управлении памятью CLR-средой с использованием сборщика мусора .NET. Будет описана роль корневых элементов приложений, поколений объектов и типа System.GC. Будет показано, как создавать самоочищаемые объекты (с применением интерфейса IDisposable) и обеспечивать процесс финализации (с помощью метода System. Object. Finalize ()). Вы узнаете о появившемся в .NET 4.0 классе Lazy<T>, который позволяет определять данные так, чтобы они не размещались в памяти до тех пор, пока вызывающая сторона не запросит их. Этот класс позволяет не загромождать память такими объектами, которые пока не требуются. Часть III. Дополнительные конструкции программирования на С# В этой части читателю предоставляется возможность углубить знания языка С# за счет изучения других более сложных (но очень важных) концепций. Здесь завершается ознакомление с системой типов .NET описанием типов делегатов и интерфейсов. Кроме того, описана роль обобщений и дано краткое введение в язык LINQ (Language Integrated Query). Также рассматриваются некоторые более сложные средства С# (такие как методы расширения, частичные методы и приемы манипулирования указателями). Глава 9. Работа с интерфейсами Материал этой главы предполагает наличие понимания концепций объектно-ориентированной разработки и посвящен программированию с использованием интерфейсов. Здесь будет показано, как определять классы и структуры, поддерживающие множество поведений, как обнаруживать эти поведения во время выполнения и как выборочно скрывать какие-то из них за счет явной реализации интерфейсов. Помимо создания специальных интерфейсов, рассматриваются вопросы реализации стандартных интерфейсов из состава .NET и их применения для построения объектов, которые могут сортироваться, копироваться, перечисляться и сравниваться.
36 Введение Глава 10. Обобщения Эта глава посвящена обобщениям. Программирование с использованием обобщений позволяет создавать типы и члены типов, содержащие заполнители, которые заполняются вызывающим кодом. В целом, обобщения позволяют значительно улучшить производительность приложений и безопасность в отношении типов. В главе рассматриваются типы обобщений из пространства имен System.Collections .Generic, а также показано, как создавать собственные обобщенные методы и типы (с ограничениями и без). Глава 11. Делегаты, события и лямбда-выражения Благодаря этой главе, станет понятно, что собой представляет тип делегата. Любой делегат в .NET представляет собой объект, который указывает на другие методы в приложении. С помощью делегатов можно создавать системы, позволяющие многочисленным объектам взаимодействовать между собой в обоих направлениях. После изучения способов применения делегатов в .NET, будет показано, как применять в С# ключевое слово event, которое упрощает манипулирование делегатами. Кроме того, рассматривается роль лямбда-операции (=>) и связь между делегатами, анонимными методами и лямбда-выражениями. Глава 12. Расширенные средства языка С# В этой главе описаны расширенные средства языка С#, в том числе перегрузка операций, создание специальных процедур преобразования (явного и неявного) для типов, построение и взаимодействие с индексаторами типов, работа с расширяющими методами, анонимными типами, частичными методами, а также указателями С# с использованием в коде контекста unsafe. Глава 13. LINQ to Object В этой главе начинается рассмотрение LINQ (Language Integrated Query — язык интегрированных запросов). Эта технология позволяет создавать строго типизированные выражения запросов, применять их к ряду различных целевых объектов LINQ и тем самым манипулировать данными в самом широком смысле этого слова. Глава посвящена API-интерфейсу LINQ to Objects, который позволяет применять LINQ-выражения к контейнерам данных (т.е. массивам, коллекциям и специальным типам). Эта информация будет полезна позже при рассмотрении других дополнительных API-интерфейсов, таких как LINQ to XML, LINQ to DataSet, PLINQ и LINQ to Entities. Часть IV. Программирование с использованием сборок .NET Эта часть книги посвящена деталям формата сборок .NET. Здесь вы узнаете не только о способах развертывания и конфигурирования библиотек кода .NET, но также о внутреннем устройстве двоичного образа .NET. Будет описана роль атрибутов .NET и определения информации о типе во время выполнения. Кроме того, рассматривается роль среды DLR (Dynamic Language Runtime — исполняющая среда динамического языка) в .NET 4.0 и ключевого слова dynamic в С# 2010. Наконец, объясняется, что собой представляют контексты объектов, как устроен CIL-код и как создавать сборки в памяти. Глава 14. Конфигурирование сборок .NET На самом высоком уровне термин сборка применяется для описания любого двоичного файла * . dll или * . ехе, который создается с помощью компилятора .NET. В действительности возможности сборок намного шире. В этой главе будет показано, чем отличаются однофайловые и многофайловые сборки, как создавать и развертывать сборки обеих разновидностей, как делать сборки приватными и разделяемыми с использовани-
Введение 37 ем XML-файлов *.configH специальных сборок политик издателя. Кроме того, в главе описан глобальный кэш сборок (Global Assembly Cache — CAG), а также изменения CAG в версии .NET 4.0. Глава 15. Рефлексия типов, позднее связывание и программирование с использованием атрибутов В главе 15 продолжается изучение сборок .NET. В ней показано, как обнаруживать типы во время выполнения с использованием пространства имен System.Reflection. Типы из этого пространства имен позволяют создавать приложения, способные считывать метаданные сборки на лету. Кроме того, в главе рассматривается динамическая загрузка и создание типов во время выполнения с помощью позднего связывания, а также роль атрибутов .NET (стандартных и специальных). Для закрепления материала в конце главы приводится пример построения расширяемого приложения Windows Forms. Глава 16. Процессы, домены приложений и контексты объектов В этой главе рассказывается о создании загруженных исполняемых файлов .NET. Целью главы является иллюстрация отношений между процессами, доменами приложений и контекстными границами. Все эти темы подготавливают базу для изучения процесса создания многопоточных приложений в главе 19. Глава 17. Язык CIL и роль динамических сборок В этой главе подробно рассматривается синтаксис и семантика языка CIL, а также роль пространства имен System. Re flection. Em it, с помощью типов из которого можно создавать программное обеспечение, способное генерировать сборки .NET в памяти во время выполнения. Формально сборки, которые определяются и выполняются в памяти, называются динамическими сборками. Их не следует путать с динамическими типами, которые являются темой главы 18. Глава 18. Динамические типы и исполняющая среда динамического языка В .NET 4.0 появился новый компонент исполняющей среды .NET, который называется исполняющей средой динамического языка (Dynamic Language Runtime — DLR). С помощью DLR в .NET и ключевого слова dynamic в С# 2010 можно определять данные, которые в действительности разрешаются во время выполнения. Использование этих средств значительно упрощает решение ряда очень сложных задач по программированию приложений .NET В главе рассматриваются некоторые практические способы применения динамических данных, включая более гладкое использование API- интерфейсы рефлексии .NET, а также упрощенное взаимодействие с унаследованными библиотеками СОМ. Часть V. Введение в библиотеки базовых классов .NET В этой части рассматривается ряд наиболее часто применяемых служб, поставляемых в составе библиотек базовых классов .NET, включая создание многопоточных приложений, файловый ввод-вывод и доступ к базам данных с помощью ADO.NET. Здесь также показано, как создавать распределенные приложения с помощью Windows Communication Foundation (WCF) и приложения с рабочими потоками, которые используют API-интерфейсы Windows Workflow Foundation (WF) и LINQ to XML. Глава 19. Многопоточность и параллельное программирование Эта глава посвящена созданию многопоточных приложений. В ней демонстрируется ряд приемов, которые можно применять для написания кода, безопасного в отноше-
38 Введение нии потоков. В начале главы кратко напоминается о том, что собой представляет тип делегата в .NET для упрощения понимания предусмотренной в нем внутренней поддержки для асинхронного вызова методов. Затем рассматриваются типы пространства имен System. Threading и новый API-интерфейс TPL (Task Parallel Library — библиотека параллельных задач), появившийся в .NET 4.O. С применением этого API-интерфейса можно создавать .NET-приложения, распределяющие рабочую нагрузку среди доступных ЦП в исключительно простой манере. В главе также описан API-интерфейс PINQ (Parallel LINQ), который позволяет создавать масштабируемые LINQ-запросы. Глава 20. Файловый ввод-вывод и сериализация объектов Пространство имен System. 10 позволяет взаимодействовать существующей структурой файлов и каталогов. В главе будет показано, как программно создавать (и удалять) систему каталогов и перемещать данные в различные потоки (файловые, строковые и находящиеся в памяти). Кроме того, рассматриваются службы .NET, предназначенные для сериализации объектов. Сериализация представляет собой процесс, который позволяет сохранять данные о состоянии объекта (или набора взаимосвязанных объектов) в потоке для использования в более позднее время, а десериализация — процесс извлечения данных о состоянии объекта из потока в память для последующего использования в приложении. В главе описана настройка процесса сериализации с применением интерфейса ISerializable и набора атрибутов .NET. Глава 21. AD0.NET, часть I: подключенный уровень В этой первой из трех посвященных базам данных главам дано введение в API- интерфейс ADO.NET. Рассматривается роль поставщиков данных .NET и взаимодействие с реляционной базой данных с применением так называемого подключенного уровня ADO.NET, который представлен объектами подключения, объектами команд, объектами транзакций и объектами чтения данных. В этой главе также приведен пример создания специальной базы данных и первой версии специальной библиотеки доступа к данным (AutoLotDAL. dll), неоднократно применяемой в остальных примерах книги. Глава 23. AD0.NET, часть II: автономный уровень В этой главе продолжается описание способов работы с базами данных и рассказывается об автономном ypoeHeADO.NET. Рассматривается роль типа DataSet и объектов адаптеров данных, а также многочисленных средств Visual Studio 2010, которые способны упростить процесс создания приложений, управляемых данными. Будет показано, как связывать объекты DataTable с элементами пользовательского интерфейса, а также как применять запросы LINQ к находящимся в памяти объектам DataSet с использованием API-интерфейса LINQ to DataSet. Глава 23. AD0.NET, часть III: Entity Framework В этой главе завершается изучение ADO.NET и рассматривается роль технологии Entity Framework (EF), которая позволяет создавать код доступа к данным с использованием строго типизированных классов, напрямую отображающихся на бизнес-модель. Здесь будут описаны роли таких входящих в состав EF компонентов, как службы объектов EF, клиент сущностей и контекст объектов. Будет показано устройство файла * . edmx, а также взаимодействие с реляционными базами данных с применением API- интерфейса LINQ to Entities. Кроме того, в главе создается последняя версия специальной библиотеки доступа к данным (AutoLotDAL.dll), которая будет использоваться в нескольких последних главах книги.
Введение 39 Глава 24. Введение в LINQ to XML В главе 14 были даны общие сведения о модели программирования LINQ и об API-интерфейсе LINQ to Objects. В этой главе читателю предлагается углубить свои знания о технологии LINQ и научиться применять запросы LINQ к XML-документам. Сначала будут рассмотрены сложности, которые существовали в .NET первоначально в области манипулирования XML-данными, на примере применения типов из сборки System.Xml. dll. Затем будет показано, как создавать XML-документы в памяти, обеспечивать их сохранение на жестком диске и перемещаться по их содержимому с использованием модели программирования LINQ (LINQ to XML). Глава 25. Введение в Windows Communication Foundation В этой главе читатель узнает об API-интерфейсе Windows Communication Foundation (WCF), который позволяет создавать распределенные приложения симметричным образом, какими бы не были лежащие в их основе низкоуровневые детали. Будет показано, как создавать службы, хосты и клиентские приложения WCF. Службы WCF являются чрезвычайно гибкими, поскольку позволяют использовать для клиентов и хостов конфигурационные файлы на основе XML, в которых декларативно задаются необходимые адреса, привязки и контракты. Кроме того, рассматриваются полезные сокращения, которые появились в .NET 4.O. Глава 26. Введение в Windows Workflow Foundation 4.0 API-интерфейс Windows Workflow Foundation (WF) вызывает больше всего путаницы у разработчиков-новичков. В версии .NET 4.0 первоначальный вариант API-интерфейса WF (появившийся в .NET 3.0) полностью переделан. В этой главе описана роль приложений, поддерживающих рабочие потоки, и способы моделирования бизнес-процессов с применением API-интерфейса WF 4.O. Рассматривается библиотека действий, поставляемая в составе WF 4.0, а также показано, как создавать специальные действия. Часть VI. Построение настольных пользовательских приложений с помощью WPF В .NET 3.0 был предложен замечательный API-интерфейс под названием Windows Presentation Foundation (WPF). Он быстро стал заменой модели программирования настольных приложений Windows Forms. WPF позволяет создавать настольные приложения с векторной графикой, интерактивной анимацией и операциями привязки данных с использованием декларативной грамматики разметки XAML. Более того, архитектура элементов управления WPF позволяет легко изменять внешний вид и поведение любого элемента управления с помощью правильно оформленного XAML-кода. В настоящем издании модели программирования WPF посвящено целых пять глав. Глава 27. Введение в Windows Presentation Foundation и XAML Технология WPF позволяет создавать чрезвычайно интерактивные и многофункциональные интерфейсы для настольных приложений (и косвенно для веб-приложений). В отличие от Windows Forms, в WPF множество ключевых служб (наподобие двухмерной и трехмерной графики, анимации, форматированных документов и т.п.) интегрируется в одну универсальную объектную модель. В главе предлагается введение в WPF и язык XAML (Extendable Application Markup Language — расширяемый язык разметки приложений). Будет показано, как создавать WPF-приложения без использования только кода без XAML, с использованием одного лишь XAML и с применением обоих подходов вместе, а также приведен пример создания специального XAML-редактора, который пригодиться при изучении остальных глав, посвященных WPF
40 Введение Глава 28. Программирование с использованием элементов управления WPF В этой главе читатель научится работать с предлагаемыми в WPF элементами управления и диспетчерами компоновки. Будет показано, как создавать системы меню, разделители окон, панели инструментов и строки состояния. Также в главе рассматриваются API-интерфейсы (и связанные с ними элементы управления), входящие в состав WPF — Documents API, Ink API и модель привязки данных. В главе приводится начальное описание IDE-среды Expression Blend, которая значительно упрощает процесс создания многофункциональных пользовательских интерфейсов для приложений WPF. Глава 29. Службы визуализации графики WPF В API-интерфейсе WPF интенсивно используется графика, в связи с чем WPF предоставляет три пути визуализации графических данных — фигуры, рисунки и визуальные объекты. В главе подробно описаны все эти пути. Кроме того, рассматривается набор важных графических примитивов (таких как кисти, перья и трансформации), а применение Expression Blend для создания графики. Также показано, как выполнять операции проверки попадания (hit-testing) в отношении графических данных. Глава 30. Ресурсы, анимация и стили WPF В этой главе освещены три важных (и связанных между собой) темы, которые позволят углубить знания API-интерфейса Windows Presentation Foundation. В первую очередь рассказывается о роли логических ресурсов. Система логических (также называемых объектными) ресурсов позволяет назначать наиболее часто используемым в WPF- приложении объектам имена и затем ссылаться на них. Кроме того, будет показано, как определять, выполнять и управлять анимационной последовательностью. И, наконец, в главе рассматривается роль стилей в WPF Подобно тому, как для веб-страниц могут применяться таблицы стилей CSS и механизм тем ASP.NET, в приложениях WPF для набора элементов управления может быть определен общий вид и поведение. Глава 31. Шаблоны элементов управления WPF и пользовательские элементы управления В этой главе завершается изучение модели программирования WPF и демонстрируется процесс создания специализированных элементов управления. Сначала рассматриваются две важных темы, связанные с созданием любого специального элемента — свойства зависимости и маршрутизируемые событиях. Затем описывается роль шаблонов по умолчанию и способы их просмотра в коде во время выполнения. Наконец, рассказывается о том, как создавать специальные классы UserControl с помощью Visual Studio 2010 и Expression Blend, в том числе и с применением .NET 4.0 Visual State Manager (VSM). Часть VII. Построение веб-приложений с использованием ASP.NET Эта часть посвящена деталям построения веб-приложений с применением API- интерфейса ASP.NET. Данный интерфейс был разработан Microsoft специально для предоставления возможности моделировать процесс создания настольных пользовательских интерфейсов путем наложения стандартного объектно-ориентированной, управляемой событиями платформы поверх стандартных запросов и ответов HTTP. Глава 32. Построение веб-страниц ASP.NET В этой главе начинается изучение процесса разработки веб-приложений с помощью ASP.NET. Как будет показано, вместо кода серверных сценариев теперь применяются самые настоящие объектно-ориентированные языки (наподобие С# и VB.NET). В главе
Введение 41 рассматривается типовой процесс создания веб-страницы ASP.NET, лежащая в основе модель программирования и другие важные аспекты ASP.NET, вроде того, как выбира- еть веб-сервер и работать с файлами Web. с on fig. Глава 33. Веб-элементы управления, мастер-страницы и темы ASP.NET В отличие от предыдущей главы, посвященной созданию объектов Page из ASP NET, в данной главе рассказывается об элементах управления, которые заполняют внутреннее дерево элементов ASP.NET. Здесь описаны основные веб-элементы управления ASP.NET, включая элементы управления проверкой достоверности, элементы управления навигацией по сайту и различные операции привязки данных. Кроме того, рассматривается роль мастер-страниц и механизма применения тем ASP.NET, который является серверным аналогом традиционных таблиц стилей. Глава 34. Управление состоянием в ASP.NET В этой главе рассматриваются разнообразные способы управления состоянием в .NET. Как и в классическом ASP, в ASP.NET можно создавать cookie-наборы и переменные уровня приложения и уровня сеанса. Кроме того, в ASP.NET есть еще одно средство для управления состоянием, которое называется кэшем приложения. Вдобавок в главе рассказывается о роли базового класса HttpApplication и демонстрируется динамическое переключение поведения веб-приложения с помощью файла Web. с on fig. Часть VIII. Приложения В этой заключительной части книги рассматриваются две темы, которые не совсем вписывались в контекст основного материала. В частности, здесь кратко рассматривается более ранняя платформа Windows Forms для построения графических интерфейсов настольных приложений, а также использование платформы Mono для создания приложений .NET, функционирующих под управлением операционных систем, отличных от Microsoft Windows. Приложение А. Программирование с помощью Windows Forms Исходный набор инструментов для построения настольных пользовательских интерфейсов, который поставляется в рамках платформы .NET с самого начала, называется Windows Forms. В этом приложении описана роль этого каркаса и показано, как с его помощью создавать главные окна, диалоговые окна и системы меню. Кроме того, здесь рассматриваются вопросы наследования форм и визуализации двухмерной графики с помощью пространства имен System. Drawing. В конце приложения приводится пример создания программы для рисования (средней сложности), иллюстрирующий практическое применение всех описанных концепций. Приложение Б. Независимая от платформы разработка .NET-приложений с помощью Mono Приложение Б посвящено использованию распространяемой с открытым исходным кодом реализации платформы .NET под названием Mono. Она позволяет разрабатывать многофункциональные приложения .NET, которые можно создавать, развертывать и выполнять под управлением самых разных операционных систем, включая Mac OS X, Solaris, AIX и многочисленные дистрибутивы Linux. Так как Mono в основном эмулирует платформу .NET от Microsoft, ее функциональные возможности вполне очевидны. Поэтому в приложении основное внимание уделено не возможностям платформы Mono, а процессу ее установки, предлагаемым в ее составе инструментам для разработки, а также используемому механизму исполняющей среды.
42 Введение Исходный код примеров Исходный код всех рассматриваемых в настоящей книге примеров доступен для загрузки на сайте издательства по адресу: http://www.williamspublishing.com От издательства Вы, читатель этой книги, и есть главный ее критик и комментатор. Мы ценим ваше мнение и хотим знать, что было сделано нами правильно, что можно было сделать лучше и что еще вы хотели бы увидеть изданным нами. Нам интересно услышать и любые другие замечания, которые вам хотелось бы высказать в наш адрес. Мы ждем ваших комментариев и надеемся на них. Вы можете прислать нам бумажное или электронное письмо, либо просто посетить наш Web-сервер и оставить свои замечания там. Одним словом, любым удобным для вас способом дайте нам знать, нравится или нет вам эта книга, а также выскажите свое мнение о том, как сделать наши книги более интересными для вас. Посылая письмо или сообщение, не забудьте указать название книги и ее авторов, а также ваш обратный адрес. Мы внимательно ознакомимся с вашим мнением и обязательно учтем его при отборе и подготовке к изданию последующих книг. Наши координаты: E-mail: info@williamspublishing.com WWW: http://www.williamspublishing.com Информация для писем из: России: 127055, г. Москва, ул. Лесная, д. 43, стр. 1 Украины: 03150, Киев, а/я 152
ЧАСТЬ I Общие сведения о языке С# и платформе .NET В этой части... Глава 1. Философия .NET ( Глава 2. Создание приложений на языке С#
ГЛАВА 1 Философия .NET Каждые несколько лет современному программисту требуется серьезно обновлять свои знания, чтобы оставаться в курсе всех последних новейших технологий. Языки (C++, Visual Basic 6.0, Java), библиотеки приложений (MFC, ATL, STL), архитектуры (COM, CORBA, EJB) и API-интерфейсы, которые провозглашались универсальными средствами для разработки программного обеспечения, постепенно начинают затмевать более совершенные или, в самом крайнем случае, более новые технологии. Какое бы чувство расстройства не возникало в связи с необходимостью обновления своей внутренней базы знаний, избежать его, честно говоря, не возможно. Поэтому целью данной книги является рассмотрение деталей тех решений, которые предлагаются Microsoft в сфере разработки программного обеспечения сегодня, а именно — деталей платформы .NET и языка программирования С#. Задача настоящей главы заключается в изложении концептуальных базовых сведений для обеспечения возможности успешного освоения всего остального материала книги. Здесь рассматриваются такие связанные с .NET концепции, как сборки, общий промежуточный язык (Common Intermediate Language — CIL) и оперативная компиляция (Just-In-Time Compilation — JIT). Помимо предварительного ознакомления с некоторыми ключевыми словами, также разъясняются взаимоотношения между различными компонентами платформы .NET, такими как общеязыковая исполняющая среда (Common Language Runtime — CLR), общая система типов (Common Type System — CTS) и общеязыковая спецификация (Common Language Specification — CLS). Кроме того, в настоящей главе описан набор функциональных возможностей, поставляемых в библиотеках базовых классов .NET 4.0, для обозначения которых иногда используют аббревиатуру BCL (Base Class Libraries — библиотеки базовых классов) или FCL (Framework Class Libraries — библиотеки классов платформы). И, наконец, напоследок в главе кратко затрагивается тема независимой от языков и платформ сущности .NET (да, это действительно так — .NET не ограничивается только операционной системой Windows). Как не трудно догадаться, многие из этих тем будут более подробно освещены в остальной части книги. Предыдущее состояние дел Прежде чем переходить к изучению специфических деталей мира .NET, не помешает узнать о некоторых вещах, которые послужили стимулом для создания Microsoft текущей платформы. Чтобы настроиться надлежащим образом, давайте начнем эту главу с краткого урока истории, чтобы вспомнить об истоках и ограничениях, которые существовали в прошлом (ведь признание существования проблемы является первым шагом на пути к ее решению). После завершения этого краткого экскурса в историю мы обратим наше внимание на те многочисленные преимущества, которые предоставляет язык С# и платформа .NET.
Глава 1. Философия .NET 45 Подход с применением языка С и API-интерфейса Windows Традиционно разработка программного обеспечения для операционных систем семейства Windows подразумевала использование языка программирования С в сочетании с API-интерфейсом Windows (Application Programming Interface — интерфейс прикладного программирования). И хотя то, что за счет применения этого проверенного временем подхода было успешно создано очень много приложений, мало кто станет спорить по поводу того, что процесс создания приложений с помощью одного только API-интерфейса является очень сложным занятием. Первая очевидная проблема состоит в том, что С представляет собой очень лаконичный язык. Разработчики программ на языке С вынуждены мириться с необходимостью "вручную" управлять памятью, безобразной арифметикой указателей и ужасными синтаксическими конструкциями. Более того, поскольку С является структурным языком программирования, ему не хватает преимуществ, обеспечиваемых объектно-ориентированным подходом (здесь можно вспомнить о спагетти-подобном коде). Из-за сочетания тысяч глобальных функций и типов данных, определенных в API-интерфейса Windows, с языком, который и без того выглядит устрашающе, совсем не удивительно, что сегодня в обиходе присутствует столь много дефектных приложений. Подход с применением языка C++ и платформы MFC Огромным шагом вперед по сравнению с подходом, предполагающим применение языка С прямо с API-интерфейсом, стал переход на использование языка программирования C++. Язык C++ во многих отношениях может считаться объектно-ориентированной надстройкой поверх языка С. Из-за этого, хотя в случае его применения программисты уже могут начинать пользоваться преимуществами известных "главных столпов ООП" (таких как инкапсуляция, наследование и полиморфизм), они все равно вынуждены иметь дело с утомительными деталями языка С (вроде необходимости осуществлять управление памятью "вручную", безобразной арифметики указателей и ужасных синтаксических конструкций). Невзирая на сложность, сегодня существует множество платформ для программирования на C++. Например, MFC (Microsoft Foundation Classes — библиотека базовых классов Microsoft) предоставляет в распоряжение разработчику набор классов C++, которые упрощают процесс создания приложений Windows. Основное предназначение MFC заключается в представлении "разумного подмножества" исходного API-интерфейса Windows в виде набора классов, "магических" макросов и многочисленных средств для автоматической генерации программного кода (обычно называемых мастерами). Несмотря на очевидную пользу данной платформы приложений (и многих других основанных на C++ наборов средств), процесс программирования на C++ остается трудным и чреватым допущением ошибок занятием из-за его исторической связи с языком С. Подход с применением Visual Basic 6.0 Благодаря искреннему желанию иметь возможность наслаждаться более простой жизнью, многие программисты перешли из "мира платформ" на базе С (C++) в мир менее сложных и более дружественных языков наподобие Visual Basic 6.0 (VB6). Язык VB6 стал популярным благодаря предоставляемой им возможности создавать сложные пользовательские интерфейсы, библиотеки программного кода (вроде СОМ-серверов) и логику доступа к базам данных с приложением минимального количества усилий. Во многом как и в MFC, в VB6 сложности API-интерфейса Windows скрываются из вида за счет предоставления ряда интегрированных мастеров, внутренних типов данных, классов и специфических функций VB.
46 Часть I. Общие сведения о языке С# и платформе .NET Главный недостаток языка VB6 (который с появлением платформы .NET был устранен) состоит в том, что он является не полностью объектно-ориентированным, а скорее — просто "объектным". Например, VB6 не позволяет программисту устанавливать между классами отношения "подчиненности" (т.е. прибегать к классическому наследованию) и не обладает никакой внутренней поддержкой для создания параметризованных классов. Более того, VB6 не предоставляет возможности для построения многопоточных приложений, если только программист не готов опускаться до уровня вызовов API-интерфейса Windows (что в лучшем случае является сложным, а в худшем — опасным подходом). На заметку! Язык Visual Basic, используемый внутри платформы .NET (и часто называемый языком VB.NET), имеет мало чего общего с языком VB6. Например, в современном языке VB поддерживается перегрузка операций, классическое наследование, конструкторы типов и обобщения. Подход с применением Java Теперь пришел черед языка Java. Язык Java представляет собой объектно-ориентированный язык программирования, который своими синтаксическими корнями уходит в C++. Как многим известно, достоинства Java не ограничиваются одной лишь только поддержкой независимости от платформ. Java как язык не имеет многих из тех неприятных синтаксических аспектов, которые присутствуют в C++, а как платформа — предоставляет в распоряжение программистам большее количество готовых пакетов с различными определениями типов внутри. За счет применения этих типов программисты на Java могут создавать "на 100% чистые Java-приложения" с возможностью подключения к базе данных, поддержкой обмена сообщениями, веб-интерфейсами и богатым настольными интерфейсами для пользователей (а также многими другими службами). Хотя Java и представляет собой очень элегантный язык, одной из потенциальных проблем является то, что применение Java обычно означает необходимость использования Java в цикле разработки и для взаимодействия клиента с сервером. Надежды на появление возможности интегрировать Java с другими языками мало, поскольку это противоречит главной цели Java — быть единственным языком программирования для удовлетворения любой потребности. В действительности, однако, в мире существуют миллионы строк программного кода, которым бы идеально подошло смешивание с более новым программным кодом на Java. К сожалению, Java делает выполнение этой задачи проблематичной. Пока в Java предлагаются лишь ограниченные возможности для получения доступа к отличным от Java API-интерфейсам, поддержка для истинной межплатформенной интеграции остается незначительной. Подход с применением СОМ Модель COM (Component Object Model — модель компонентных объектов) была предшествующей платформой для разработки приложений, которая предлагалась Microsoft, и впервые появилась в мире программирования приблизительно в 1991 г. (или в 1993 г., если считать моментом ее появления рождение версии OLE 1.0). Она представляет собой архитектуру, которая, по сути, гласит следующее: в случае построения типов в соответствии с правилами СОМ, будет получаться блок многократно используемого двоичного кода. Такие двоичные блоки кода СОМ часто называют "серверами СОМ". Одним из главным преимуществ двоичного СОМ-сервера является то, что к нему можно получать доступ независимым от языка образом. Это означает, что программисты на C++ могут создавать СОМ-классы, пригодные для использования в VB6, программисты на Delphi — применять СОМ-классы, созданные с помощью С, и т.д. Однако,
Глава 1. Философия .NET 47 как не трудно догадаться, подобная независимость СОМ от языка является несколько ограниченной. Например, никакого способа для порождения нового СОМ-класса с использованием уже существующего не имеется (поскольку СОМ не обладает поддержкой классического наследования). Вместо этого для использования типов СОМ-класса требуется задавать несколько неуклюжее отношение принадлежности (has-a). Еще одним преимуществом СОМ является прозрачность расположения. За счет применения конструкций вроде системного реестра, идентификаторов приложений (AppID), заглушек, прокси-объектов и исполняющей среды СОМ программисты могут избегать необходимости иметь дело с самими сокетам, RPC-вызовам и другими низкоуровневыми деталями при создании распределенного приложения. Например, рассмотрим следующий программный код СОМ-клиента наУВб: ' Данный тип MyCOMClass мог бы быть написан на любом ' поддерживающем СОМ языке и размещаться в любом месте ' в сети (в том числе и на локальной машине) . Dim obj as MyCOMClass Set obj = New MyCOMClass ' Местонахождение определяется с помощью AppID. obj.DoSomeWork Хотя COM и можно считать очень успешной объектной моделью, ее внутреннее устройство является чрезвычайно сложным (и требует затрачивания программистами многих месяцев на его изучение, особенно теми, которые программируют на C++). Для облегчения процесса разработки двоичных СОМ-объектов программисты могут использовать многочисленные платформы, поддерживающие СОМ. Например, в ATL (Active Template Library — библиотека активных шаблонов) для упрощения процесса создания СОМ- серверов предоставляется набор специальных классов, шаблонов и макросов на C++. Во многих других языках приличная часть инфраструктуры СОМ тоже скрывается из вида. Поддержки одного только языка, однако, для сокрытия всей сложности СОМ не хватает. Даже при выборе относительно простого поддерживающего СОМ языка вроде VB6, все равно требуется бороться с "хрупкими" записями о регистрации и многочисленными деталями развертывания (в совокупности несколько саркастично называемыми адом DLL). Сложность представления типов данных СОМ Хотя СОМ, несомненно, упрощает процесс создания программных приложений с помощью различных языков программирования, независимая от языка природа СОМ не является настолько простой, насколько возможно хотелось бы. Некоторая доля этой сложности является следствием того факта, что приложения, которые сплетаются вместе с помощью разнообразных языков, получаются совершенно не связанными с синтаксической точки зрения. Например, синтаксис JScript во многом похож на синтаксис С, а синтаксис VBScript представляет собой подмножество синтаксиса VB6. СОМ-серверы, которые создаются для выполнения в исполняющей среде СОМ+ (представляющей собой компонент операционной системы Windows, который предлагает общие службы для библиотек специального кода, такие как транзакции, жизненный цикл объектов, безопасность и т.д.), имеют совершенно не такой вид и поведение, как ориентированные на использование в веб-сети ASP-страницы, в которых они вызываются. В результате получается очень запутанная смесь технологий. Более того, что, пожалуй, даже еще важнее, каждый язык и/или технология обладает собственной системой типов (которая может быть совершенно не похожа на систему типов другого языка или технологии). Помимо того факта, что каждый API-интерфейс поставляется с собственной коллекцией готового кода, даже базовые типы данных могут не всегда интерпретироваться идентичным образом. Например, тип CComBSTR в ATL
48 Часть I. Общие сведения о языке С# и платформе .NET представляет собой не совсем то же самое, что тип String в VB6, и оба они не имеют совершенно ничего общего с типом char* в С. Из-за того, что каждый язык обладает собственной уникальной системой типов, СОМ-программистам обычно требуется соблюдать предельную осторожность при создании общедоступных методов в общедоступных классах СОМ. Например, при возникновении у разработчика на C++ необходимости в создании метода, способного возвращать массив целых чисел в приложении VB6, ему пришлось бы полностью погружаться в сложные вызовы API-интерфейса СОМ для построения структуры SAFE ARRAY, которое вполне могло бы потребовать написания десятков строк кода. В мире СОМ тип данных SAFEARRAY является единственным способом для создания массива, который могли бы распознавать все платформы СОМ. Если разработчик на C++ вернет просто собственный массив C++, у приложения VB6 не будет никакого представления о том, что с ним делать. Подобные сложности могут возникать и при построении методов, предусматривающих выполнение манипуляций над простыми строковыми данными, ссылками на другие объекты СОМ и даже обычными булевскими значениями. Мягко говоря, программирование с использованием СОМ является очень несимметричной дисциплиной. Решение .NET Немало информации для короткого урока истории. Главное понять, что жизнь программиста Windows-приложений раньше была трудной. Платформа .NET Framework являет собой достаточно радикальную "силовую" попытку сделать жизнь программистов легче. Как можно будет увидеть в остальной части настоящей книги, .NET Framework представляет собой программную платформу для создания приложений на базе семейства операционных систем Windows, а также многочисленных операционных систем производства не Microsoft, таких как Mac OS X и различные дистрибутивы Unix и Linux. Для начала не помешает привести краткий перечень некоторых базовых функциональных возможностей, которыми обладает .NET • Возможность обеспечения взаимодействия с существующим программным кодом. Эта возможность, несомненно, является очень хорошей вещью, поскольку позволяет комбинировать существующие двоичные единицы СОМ (т.е. обеспечивать их взаимодействие) с более новыми двоичными единицами .NET и наоборот. С выходом версии .NET 4.0 эта возможность стала выглядеть даже еще проще, благодаря добавлению ключевого слова dynamic (о котором будет более подробно рассказываться в главе 18). • Поддержка для многочисленных языков программирования. Приложения .NET можно создавать с помощью любого множества языков программирования (С#, Visual Basic, F#, S# и т.д.). • Общий исполняющий механизм, используемый всеми поддерживающими .NET языками. Одним из аспектов этого механизма является наличие хорошо определенного набора типов, которые способен понимать каждый поддерживающий .NET язык. • Полная и тотальная интеграция языков. В .NET поддерживается межъязыковое наследование, межъязыковая обработка исключений и межъязыковая отладка кода. • Обширная библиотека базовых классов. Эта библиотека позволяет избегать сложностей, связанных с выполнением прямых вызовов к API-интерфейсу, и предлагает согласованную объектную модель, которую могут использовать все поддерживающие .NET языки.
Глава 1. Философия .NET 49 • Отсутствие необходимости в предоставлении низкоуровневых деталей СОМ. В двоичной единице .NET нет места ни для интерфейсов IClassFactory, IUnknown и IDispatch, ни для кода IDL, ни для вариантных типов данных (подобных BSTR, SAFEARRAY и т.д.). • Упрощенная модель развертывания. В .NET нет никакой необходимости заботиться о регистрации двоичной единицы в системном реестре. Более того, в .NET позволяется делать так, чтобы многочисленные версии одной и той же сборки * , dll могли без проблем сосуществовать на одной и той же машине. Как не трудно догадаться по приведенному выше перечню, платформа .NET не имеет ничего общего с СОМ (за исключением разве что того факта, что обе этих платформы являются детищем Microsoft). На самом деле единственным способом, которым может обеспечиваться взаимодействие между типами .NET и СОМ, будет использование уровня функциональной совместимости. Главные компоненты платформы .NET (CLR, CTS и CLS) Теперь, когда о некоторых из предоставляемых .NET преимуществах уже известно, давайте вкратце ознакомимся с тремя ключевыми (и связанными между собой) сущностями, которые делают предоставление этих преимуществ возможным: CLR, CTS и CLS. С точки зрения программиста .NET представляет собой исполняющую среду и обширную библиотеку базовых классов. Уровень исполняющей среды называется общеязыковой исполняющей средой (Common Language Runtime) или, сокращенно, средой CLR. Главной задачей CLR является автоматическое обнаружение, загрузка и управление типами .NET (вместо программиста). Кроме того, среда CLR заботится о ряде низкоуровневых деталей, таких как управление памятью, обслуживание приложения, обработка потоков и выполнение различных проверок, связанных с безопасностью. Другим составляющим компонентом платформы .NET является общая система типов (Common Type System) или, сокращенно, система CTS. В спецификации CTS представлено полное описание всех возможных типов данных и программных конструкций, поддерживаемых исполняющей средой, того, как эти сущности могут взаимодействовать друг с другом, и того, как они могут представляться в формате метаданных .NET (которые более подробно рассматриваются далее в этой главе и полностью — в главе 15). Важно понимать, что любая из определенных в CTS функциональных возможностей может не поддерживаться в отдельно взятом языке, совместимом с .NET. Поэтому существует еще общеязыковая спецификация (Common Language Specification) или, сокращенно, спецификация CLS, в которой описано лишь то подмножество общих типов и программных конструкций, каковое способны воспринимать абсолютно все поддерживающие .NET языки программирования. Следовательно, в случае построения типов .NET только с функциональными возможностями, которые предусмотрены в CLS, можно оставаться полностью уверенным в том, что все совместимые с .NET языки смогут их использовать. И, наоборот, в случае применения такого типа данных или конструкции программирования, которой нет в CLS, рассчитывать на то, что каждый язык программирования .NET сможет взаимодействовать с подобной библиотекой кода .NET, нельзя. К счастью, как будет показано позже в этой главе, существует очень простой способ указывать компилятору С#, чтобы он проверял весь код на предмет совместимости с CLS.
50 Часть I. Общие сведения о языке С# и платформе .NET Роль библиотек базовых классов Помимо среды CLR и спецификаций CTS и CLS, в составе платформы .NET поставляется библиотека базовых классов, которая является доступной для всех языков программирования .NET. В этой библиотеке не только содержатся определения различных примитивов, таких как потоки, файловый ввод-вывод, системы графической визуализации и механизмы для взаимодействия с различными внешними устройствами, но также предоставляется поддержка для целого ряда служб, требуемых в большинстве реальных приложений. Например, в библиотеке базовых классов содержатся определения типов, которые способны упрощать процесс получения доступа к базам данных, манипулирования XML-документами, обеспечения программной безопасности и создания веб-, а также обычных настольных и консольных интерфейсов. На высоком уровне взаимосвязь между CLR, CTS, CLS и библиотекой базовых классов выглядит так, как показано на рис. 1.1. Доступ к базе данных Организация потоковой обработки Библиотека базовых классов Настольные графические API-интерфейсы Файловый ввод-вывод Безопасность API-интерфейсы для работы с веб-содержимым API-интерфейсы для удаленной работы и другие Общеязыковая исполняющая среда (CLR) Общая система типов (CTS) Общеязыковая спецификация (CLS) Рис. 1.1. Отношения между CLR, CTS, CLS и библиотеками базовых классов Что привносит язык С# Из-за того, что платформа .NET столь радикально отличается от предыдущих технологий, в Microsoft разработали специально под нее новый язык программирования С#. Синтаксис этого языка программирования очень похож на синтаксис языка Java. Однако сказать, что С# просто переписан с Java, будет неточно. И язык С#, и язык Java просто оба являются членами семейства языков программирования С (в которое также входят языки С, Objective С, C++ и т.д.) и потому имеют схожий синтаксис. Правда состоит в том, что многие синтаксические конструкции в С# моделируются согласно различным особенностям Visual Basic 6.0 и C++. Например, как и в VB6, в С# поддерживается понятие формальных свойств типов (в противоположность традиционным методам get и set) и возможность объявлять методы, принимающие переменное количество аргументов (через массивы параметров). Как и в C++, в С# допускается перегружать операции, а также создавать структуры, перечисления и функции обратного вызова (посредством делегатов).
Глава 1. Философия .NET 51 Более того, по мере изучения излагаемого в этой книге материала, можно будет быстро заметить, что в С# поддерживается целый ряд функциональных возможностей, которые традиционно встречаются в различных функциональных языках программирования (например, LISP или Haskell) и к числу которых относятся лямбда-выражения и анонимные типы. Кроме того, с появлением технологии LINQ в С# стали поддерживаться еще и конструкции, которые делают его довольно уникальным в мире программирования. Несмотря на все это, наибольшее влияние на него все-таки оказали именно языки на базе С. Благодаря тому факту, что С# представляет собой собранный из нескольких языков гибрид, он является таким же "чистым" с синтаксической точки зрения, как и язык Java (а то и "чище" его), почти столь же простым, как язык VB6, и практически таким же мощным и гибким как C++ (только без ассоциируемых с ним громоздких элементов). Ниже приведен неполный список ключевых функциональных возможностей языка С#, которые присутствуют во всех его версиях. • Никаких указателей использовать не требуется! В программах на С# обычно не возникает необходимости в манипулировании указателями напрямую (хотя опуститься к этому уровню все-таки можно, как будет показано в главе 12). • Управление памятью осуществляется автоматически посредством сборки мусора. По этой причине ключевое слово delete в С# не поддерживается. • Предлагаются формальные синтаксические конструкции для классов, интерфейсов, структур, перечислений и делегатов. • Предоставляется аналогичная C++ возможность перегружать операции для пользовательских типов, но без лишних сложностей (например, заботиться о "возврате *this для обеспечения связывания" не требуется). • Предлагается поддержка для программирования с использованием атрибутов. Такой подход в сфере разработки позволяет снабжать типы и их членов аннотациями и тем самым еще больше уточнять их поведение. С выходом версии .NET 2.0 (примерно в 2005 г.), язык программирования С# был обновлен и стал поддерживать многочисленные новые функциональные возможности, наиболее заслуживающие внимания из которых перечислены ниже. • Возможность создавать обобщенные типы и обобщенные элементы-члены. За счет применения обобщений можно создавать очень эффективный и безопасный для типов код с многочисленными метками-заполнителями, подстановка значений в которые будет происходить в момент непосредственного взаимодействия с данным обобщенным элементом. • Поддержка для анонимных методов, каковые позволяют предоставлять встраиваемую функцию везде, где требуется использовать тип делегата. • Многочисленные упрощения в модели "делегат-событие", в том числе возможность применения ковариантности, контравариантности и преобразования групп методов. (Если какие-то из этих терминов пока не знакомы, не стоит пугаться; все они подробно объясняются далее в книге.) • Возможность определять один тип в нескольких файлах кода (или, если необходимо, в виде представления в памяти) с помощью ключевого слова partial. В версии .NET 3.5 (которая вышла примерно в 2008 г.) в язык программирования С# снова были добавлены новые функциональные возможности, наиболее важные из которых описаны ниже. • Поддержка для строго типизированных запросов (также называемых запросами LINQ), которые применяются для взаимодействия с различными видами данных.
52 Часть I. Общие сведения о языке С# и платформе .NET • Поддержка для анонимных типов, которые позволяют моделировать форму типа, а не его поведение. • Возможность расширять функциональные возможности существующего типа с помощью методов расширения. • Возможность использовать лямбда-операцию (=>), которая даже еще больше упрощает работу с типами делегатов в .NET. • Новый синтаксис для инициализации объектов, который позволяет устанавливать значения свойств во время создания объектов. В текущем выпуске платформы .NET версии 4.0 язык С# был опять обновлен и дополнен рядом новых функциональных возможностей. Хотя приведенный ниже перечень новых конструкций может показаться довольно ограниченным, по ходу прочтения настоящей книги можно будет увидеть, насколько полезными они могут оказаться. • Поддержка необязательных параметров в методах, а также именованных аргументов. • Поддержка динамического поиска членов во время выполнения посредством ключевого слова dynamic. Как будет показано в главе 18, эта поддержка предоставляет в распоряжение универсальный подход для осуществления вызова членов "на лету", с помощью какой бы платформы они не были реализованы (COM, IronRuby, IronPython, HTML DOM или службы рефлексии .NET). , • Вместе с предыдущей возможностью в .NET 4.0 значительно упрощается обеспечение взаимодействия приложений на С# с унаследованными серверами СОМ, благодаря устранению зависимости от сборок взаимодействия (interop assemblies) и предоставлению поддержки необязательных аргументов ref. • Работа с обобщенными типами стала гораздо понятнее, благодаря появлению возможности легко отображать обобщенные данные на и из общих коллекций System.Object с помощью ковариантности и контравариантности. Возможно, наиболее важным моментом, о котором следует знать, программируя на С#, является то, что с помощью этого языка можно создавать только такой код, который будет выполняться в исполняющей среде .NET (использовать С# для построения "классического" СОМ-сервера или неуправляемого приложения с вызовами API-интерфейса и кодом на С и C++ нельзя). Официально код, ориентируемый на выполнение в исполняющей среде .NET, называется управляемым кодом (managed code), двоичная единица, в которой содержится такой управляемый код — сборкой (assembly; о сборках будет более подробно рассказываться позже в настоящей главе), а код, который не может обслуживаться непосредственно в исполняющей среде .NET — неуправляемым кодом (unma- naged code). Другие языки программирования с поддержкой .NET Следует понимать, что С# является не единственным языком, который может применяться для построения .NET-приложений. При установке доступного для бесплатной загрузки комплекта разработки программного обеспечения Microsoft .NET 4.0 Framework Software Development Kit (SDK), равно как и при установке Visual Studio 2010, для выбора становятся доступными пять управляемых языков: С#, Visual Basic, C++/CLI, JScript .NET и F#.
Глава 1. Философия .NET 53 На заметку! F# — это новый язык .NET, основанный на семействе функциональных языков ML и главным образом — на OCaml. Хотя он может применяться в качестве чисто функционального языка, в нем также предлагается поддержка для конструкций ООП и библиотек базовых классов .NET. Тем,.кому интересно узнать больше о нем, могут посетить его официальную веб-страницу по следующему адресу: http: //msdn. microsoft. com/f sharp. Помимо управляемых языков, предлагаемых Microsoft, существуют .NET-компиля- торы, которые предназначены для таких языков, как Smalltalk, COBOL и Pascal (и это далеко не полный перечень). Хотя в настоящей книге все внимание практически полностью уделяется лишь С#, следующий веб-сайт тоже вызвать интерес: http://www.dotnetlanguages.net Щелкнув на ссылке Resources (Ресурсы) в самом верху домашней страницы этого сайта, можно получить доступ к списку всех языков программирования .NET и соответствующих ссылок, по которым для них можно загружать различные компиляторы (рис. 1.2). C # - Page- Safety- al Р Д Mercury ItonPyUmn p* DEDICATED .NET Languages ™ Forth SML.NET S# Ron Nermji k INVESTIG News | FAQ J Resources | Contact | RSS Feed | Recent Comments Feed Resources Following is a listing of resources that you may find useful either to own or bookmark during your navigation through the .NET language space. If you feel that there's a resource that other .NET developers should know about, please contact nw. .NET Language Sites • At» • APL ч • ASP.NET: ASM to И. • AsmL i • ASP (Gotham) • Bask о VB .NET (Microsoft) о V8.NET (Mono) • BETA • Boo • BtueOragon • С о tec Ф Internet! Protected Mode On fu -r ^100% Рис. 1.2. Один из многочисленных сайтов с документацией по известным языкам программирования .NET Хотя настоящая книга ориентирована главным образом на тех, кого интересует разработка программ .NET с использованием С#, все равно рекомендуется посетить указанный сайт, поскольку там наверняка можно будет найти много языков для .NET, заслуживающих отдельного изучения в свободное время (вроде LISP .NET).
54 Часть I. Общие сведения о языке С# и платформе .NET Жизнь в многоязычном окружении В начале процесса осмысления разработчиком нейтральной к языкам природы платформы .NET, у него возникает множество вопросов и, прежде всего, следующий: если все языки .NET при компиляции преобразуются в управляемый код, то почему существует не один, а множество компиляторов? Ответить на этот вопрос можно по-разному. Мы, программисты, бываем очень привередливы, когда дело касается выбора языка программирования. Некоторые предпочитают языки с многочисленными точками с запятой и фигурными скобками, но с минимальным набором ключевых слов. Другим нравятся языки, предлагающие более "человеческие" синтаксические лексемы (вроде языка Visual Basic). Кто-то не желает отказываться от своего опыта работы на мэйнфреймах и предпочитает переносить его и на платформу .NET (использовать COBOL .NET). А теперь ответьте честно: если бы в Microsoft предложили единственный "официальный" язык .NET, например, на базе семейства BASIC, то все ли программисты были бы рады такому выбору? Или если бы "официальный" язык .NET основывался на синтаксисе Fortran, то сколько людей в мире просто бы проигнорировало платформу .NET? Поскольку среда выполнения .NET демонстрирует меньшую зависимость от языка, используемого для построения управляемого программного кода, программисты .NET могут, не меняя своих синтаксических предпочтений, обмениваться скомпилированными сборками со своими коллегами, другими отделами и внешними организациями (не обращая внимания на то, какой язык .NET в них применяется). Еще одно полезное преимущество интеграции различных языков .NET в одном унифицированном программном решении вытекает из того простого факта, что каждый язык программирования имеет свои сильные (а также слабые) стороны. Например, некоторые языки программирования обладают превосходной встроенной поддержкой сложных математических вычислений. В других лучше реализованы финансовые или логические вычисления, взаимодействие с мэйнфреймами и т.п. А когда преимущества конкретного языка программирования объединяются с преимуществами платформы .NET, выигрывают все. Конечно, в реальности велика вероятность того, что будет возможность тратить большую часть времени на построение программного обеспечения с помощью предпочитаемого языка .NET. Однако, после освоения синтаксиса одного из языков .NET, изучение синтаксиса какого-то другого языка существенно упрощается. Вдобавок это довольно выгодно, особенно тем, кто занимается консультированием по разработке ПО. Например, тому, у кого предпочитаемым языком является С#, в случае попадания в клиентскую среду, где все построено на Visual Basic, это все равно позволит эксплуатировать функциональные возможности .NET Framework и разбираться в общей структуре кодовой базы с минимальным объемом усилий и беспокойства. Сказанного вполне достаточно. Что собой представляют сборки в .NET Какой бы язык .NET не выбирался для программирования, важно понимать, что хотя двоичные ^ЕТ-единицы имеют такое же файловое расширение, как и двоичные единицы СОМ-серверов и неуправляемых программ Win32 (* . dll или * . ехе), внутренне они устроены абсолютно по-другому. Например, двоичные ^ЕТ-единицы * .dll не экспортируют методы для упрощения взаимодействия с исполняющей средой СОМ (поскольку .NET — это не СОМ). Более того, они не описываются с помощью библиотек СОМ-типов и не регистрируются в системном реестре. Пожалуй, самым важным является то, что они содержат не специфические, а наоборот, не зависящие от платформы инструкции на промежуточном языке (Intermediate Language — IL), а также метаданные типов. На рис. 1.3 показано, как все это выглядит схематически.
Исходный код наС# Исходный код HaPerl.NET Исходный код HaCOBOL.NET Исходный код на C++/CLI Глава 1. Философия .NET 55 Компилятор С# Компилятор Perl .NET Компилятор COBOL .NET Компилятор C++/CLI Инструкции IL и метаданные (* .dll или *.ехе) Рис. 1.3. Все .NET-компиляторы генерируют IL-инструкции и метаданные На заметку! Относительно сокращения "IL" уместно сказать несколько дополнительных слов. В ходе разработки .NET официальным названием для IL было Microsoft Intermediate Language (MSIL). Однако в вышедшей последней версии .NET это название было изменено на CIL (Common Intermediate Language — общий промежуточный язык). Поэтому при прочтении литературы по .NET следует помнить о том, что IL, MSIL и CIL обозначают одно и то же. Для отражения современной терминологии в настоящей книге будет применяться аббревиатура CIL. При создании файла * .dll или * . ехе с помощью .NET-компилятор а получаемый большой двоичный объект называется сборкой (assembly). Все многочисленные детали .NET-сборок будет подробно рассматриваться в главе 14. Для облегчения повествования об исполняющей среде здесь, однако, все-таки необходимо рассказать хотя бы об основных свойствах этого нового формата файлов. Как уже упоминалось, в сборке содержится CIL-код, который концептуально похож на байт-код Java тем, что не компилируется в ориентированные на конкретную платформу инструкции до тех пор, пока это не становится абсолютно необходимым. Обычно этот момент "абсолютной необходимости" наступает тогда, когда к какому-то блоку CIL- инструкций (например, к реализации метода) выполняется обращение для его использования в исполняющей среде .NET. Помимо CIL-инструкций, в сборках также содержатся метаданные, которые детально описывают особенности каждого имеющегося внутри данной двоичной .NET- единицы "типа". Например, при наличии класса по имени SportsCar они будут описывать детали наподобие того, как выглядит базовый класс этого класса SportsCar, какие интерфейсы реализует SportsCar (если вообще реализует), а также, подробно, какие члены он поддерживает Метаданные .NET всегда предоставляются внутри сборки и автоматически генерируются компилятором соответствующего распознающего .NET языка. И, наконец, помимо CIL и метаданных типов, сами сборки тоже описываются с помощью метаданных, которые официально называются манифестом (manifest). В каждом таком манифесте содержится информация о текущей версии сборки, сведения о культуре (применяемые для локализации строковых и графических ресурсов) и перечень ссылок на все внешние сборки, которые требуются для правильного функционирования. Разнообразные инструменты, которые можно использовать для изучения типов, метаданных и манифестов сборок, рассматриваются в нескольких последующих главах.
56 Часть I. Общие сведения о языке С# и платформе .NET Однофайловые и многофайловые сборки В большом количестве случаев между сборками .NET и файлами двоичного кода (* . dll или * . ехе) соблюдается простое соответствие "один к одному". Следовательно, получается, что при построении * . dll-библиотеки .NET, можно спокойно полагать, что файл двоичного кода и сборка представляют собой одно и то же, и что, аналогичным образом, при построении исполняемого приложения для настольной системы на файл * . ехе можно ссылаться как на саму сборку. Однако, как будет показано в главе 14, это не совсем так. С технической точки зрения, сборка, состоящая из одного единственного модуля * . dll или * . ехе, называется однофайловой сборкой. В однофайловых сборках все необходимые CIL-инструкции, метаданные и манифесты содержатся в одном автономном четко определенном пакете. Многомофайловые сборки, в свою очередь, состоят из множества файлов двоичного кода .NET, каждый из которых называется модулем (module). При построении многофайловой сборки в одном из ее модулей (называемом первичным или главным (primary) модулем) содержится манифест всей самой сборки (и, возможно, CIL-инструкции и метаданные по различным типам), а во всех остальных — манифест, CIL-инструкции и метаданные типов, охватывающие уровень только соответствующего7 модуля. Как нетрудно догадаться, в главном модуле содержится описание набора требуемых дополнительных модулей внутри манифеста сборки. На заметку! В главе 14 будет более подробно разъясняться, в чем состоит различие между одно- файловыми и многофайловыми сборками. Однако следует иметь в виду, что Visual Studio 2010 может применяться только для создания однофайловых сборок. В тех редких случаях возникновения необходимости в создании именно многофайловой сборки требуется использовать соответствующие утилиты командной строки. Роль CIL Теперь давайте немного более подробно посмотрим, что же собой представляет CIL- код, метаданные типов и манифест сборки. CIL является таким языком, который стоит выше любого конкретного набора ориентированных на определенную платформу инструкций. Например, ниже приведен пример кода на С#, в котором создается модель самого обычного калькулятора. Углубляться в конкретные детали синтаксиса пока не нужно, главное обратить внимание на формат такого метода в этом классе Calc, как Add(). //Класс Calc.cs using System; namespace CalculatorExample { //В этом классе содержится точка для входа в приложение. class Program { static void Main() { Calc с = new Calc () ; int ans = с Add A0, 84); Console.WriteLine(0 + 84 is {0 } . ", ans); // Обеспечение ожидания нажатия пользователем // клавиши <Enter> перед выходом. Console.ReadLine(); / / Калькулятор на С#. class Calc
Глава 1. Философия .NET 57 { public int Add(int x, int y) { return x + y; } } } После выполнения компиляции файла с этим кодом с помощью компилятора С# (csc.exe) получится однофайловая сборка * .ехе, в которой будет содержаться манифест, CIL-инструкции и метаданные, описывающие каждый из аспектов класса Calc и Program. На заметку! О том, как выполнять компиляцию кода с помощью компилятора С#, а также использовать графические IDE-среды, подобные Microsoft Visual Studio 2010, Microsoft Visual C# 2010 Express и SharpDevelop, будет более подробно рассказываться в главе 2. Например, открыв данную сборку в утилите ildasm.exe (которая более подробно рассматривается далее в настоящей главе), можно увидеть, что метод Add () был преобразован в CIL так, как показано ниже: .method public hidebysig instance int32 Add(int32 x, int32 y) cil managed { // Code size 9 @x9) // Размер кода 9 @x9) .maxstack 2 .locals mit (int32 V_0) IL_0000: nop IL_0001: ldarg.l IL_0002: ldarg.2 IL_0003: add IL_0004: stloc.O IL_0005: br.s IL_0007 IL_0007: ldloc.O IL_0008: ret } // end of method Calc::Add // конец метода Calc::Add He стоит беспокоиться, если пока совершенно не понятно, что собой представляет результирующий CIL-код этого метода, потому что в главе 17 будут рассматриваться все необходимые базовые аспекты языка программирования CIL. Главное заметить, что компилятор С# выдает CIL-код, а не ориентированные на определенную платформу инструкции. Теперь напоминаем, что так себя ведут все .NET-компиляторы. Чтобы убедиться в этом, давайте попробуем создать то же самое приложение с использованием языка Visual Basic, а не С#. 'Класс Calc.vb Imports System Namespace CalculatorExample 1 В VB "модулем" называется класс, в котором 1 содержатся только статические члены. Module Program Sub Main () Dim с As New Calc Dim ans As Integer = c.AddA0, 84) Console.WriteLine(0 + 84 is {0}.", ans) Console.ReadLine() End Sub End Module Class Calc
58 Часть I. Общие сведения о языке С# и платформе .NET Public Function Add(ByVal x As Integer, ByVal у As Integer) As Integer Return x + у End Function End Class End Namespace В случае изучения CIL-кода этого метода Add () можно будет обнаружить похожие инструкции (лишь слегка подправленные компилятором Visual Basic, vbc . exe): .method public instance int32 Add(int32 x, int32 y) cil managed { // Code size 8 @x8) // Размер кода 8 @x8) .maxstack 2 .locals mit (int32 V_0) IL_0000: ldarg.l IL_0001: ldarg.2 IL_0002: add.ovf IL_0003: stloc.O IL_0004: br.s IL_0006 IL_0006: ldloc.O IL_0007: ret } // end of method Calc: :Add // конец метода Calc::Add Исходный код. Файлы с кодом Calc . cs и Calc . vb доступны в подкаталоге Chapter l. Преимущества CIL На этом этапе может возникнуть вопрос о том, какую выгоду приносит компиляция исходного кода в CIL, а не напрямую в набор ориентированных на конкретную платформу инструкций. Одним из самых важных преимуществ такого подхода является интеграция языков. Как уже можно было увидеть, все компиляторы .NET генерируют примерно одинаковые CIL-инструкции. Благодаря этому все языки могут взаимодействовать в рамках четко обозначенной двоичной "арены". Более того, поскольку CIL не зависит от платформы, .NET Framework тоже получается не зависящей от платформы, предоставляя те же самые преимущества, к которым привыкли Java-разработчики (например, единую кодовую базу, способную работать во многих операционных системах). На самом деле уже существует международный стандарт языка С#, а также подмножество платформы .NET и реализации для многих операционных систем, отличных от Windows (более подробно об этом речь пойдет в конце настоящей главы). В отличие от Java, однако, .NET позволяет создавать приложения на предпочитаемом языке. Компиляция CIL-кода в инструкции, ориентированные на конкретную платформу Из-за того, что в сборках содержатся CIL-инструкции, а не инструкции, ориентированные на конкретную платформу, CIL-код перед использованием должен обязательно компилироваться на лету. Объект, который отвечает за компиляцию CIL-кода в понятные ЦП инструкции, называется оперативным dust-in-time — JIT) компилятором. Иногда его "по-дружески" называют Jitter. Исполняющая среда .NET использует JIT-компилятор в соответствии с целевым ЦП и оптимизирует его согласно лежащей в основе платформе.
Глава 1. Философия .NET 59 Например, в случае создания .NET-приложения, предназначенного для развертывания на карманном устройстве (например, на мобильном устройстве, функционирующем под управлением Windows), соответствующий JIT-компилятор будет оптимизирован под функционирование в среде с ограниченным объемом памяти, а в случае развертывания сборки на серверной системе (где объем памяти редко представляет проблему), наоборот — под функционирование в среде с большим объемом памяти. Это дает разработчикам возможность писать единственный блок кода, который будет автоматически эффективно компилироваться JIT-компилятором и выполняться на машинах с разной архитектурой. Более того, при компиляции CIL-инструкций в соответствующий машинный код JIT- компилятор будет помещать результаты в кэш в соответствии с тем, как того требует целевая операционная система. То есть при вызове, например, метода PrintDocument () в первый раз соответствующие С11>инструкции будут компилироваться в ориентированные на конкретную платформу инструкции и сохраняться в памяти для последующего использования, благодаря чему при вызове PrintDocument () в следующий раз компилировать их снова не понадобится. На заметку! Можно также выполнять "предварительную ЛТ-компиляцию" при инсталляции приложения с помощью утилиты командной строки ngen.exe, которая поставляется в составе набора .NET Framework 4.0 SDK. Применение такого подхода позволяет улучшить показатели по времени запуска для приложений, насыщенных графикой. Роль метаданных типов в .NET Помимо CIL-инструкций, в сборке .NET содержатся исчерпывающие и точные метаданные, которые описывают каждый определенный в двоичном файле тип (например, класс, структуру или перечисление), а также всех его членов (например, свойства, методы или события). К счастью, за генерацию новейших и наилучших метаданных по типам всегда отвечает компилятор, а не программист. Из-за того, что метаданные .NET являются настолько детальными, сборки представляют собой полностью самоописываемые (self-describing) сущности. Чтобы увидеть, как выглядит формат метаданных типов в .NET, давайте рассмотрим метаданные, которые были сгенерированы для приведенного выше метода Add () из класса Calc на языке С# (метаданные для версии метода Add () на языке Visual Basic будут выглядеть похоже): TypeDef #2 @2000003) TypDefName: CalculatorExample.Calc @2000003) Flags : [NotPublic] [AutoLayout] [Class] [AnsiClass] [BeforeFieldlnit] @0100001) Extends : 01000001 [TypeRef] System.Object Method #1 @6000003) Methodllame Flags PVA ImplFlags CallCnvntn hasThis ReturnType: 14 2 Arguments Argument #1: 14 Argument #2: 14 Add @6000003) [Public] [HideBySig] [ReuseSlot] @0000086) 0x00002090 [IL] [Managed] @0000000) [DEFAULT]
60 Часть I. Общие сведения о языке С# и платформе .NET 2 Parameters A) ParamToken : @8000001) Name : x flags: [none] @0000000) B) ParamToken : @8000002) Name : у flags: [none] @0000000) Метаданные используются во многих операциях самой исполняющей среды .NET, a также в различных средствах разработки. Например, функция IntelliSense, предлагаемая в таких средствах, как Visual Studio 2010, работает за счет считывания метаданных сборки во время проектирования. Кроме того, метаданные используются в различных утилитах для просмотра объектов, инструментах отладки и в самом компиляторе языка С#. Можно с полной уверенностью утверждать, что метаданные играют ключевую роль во многих .NET-технологиях, в том числе в Windows Communication Foundation (WCF), рефлексии, динамическом связывании и сериализации объектов. Более подробно о роли метаданных .NET будет рассказываться в главе 17. Роль манифеста сборки И, наконец, последним, но не менее важным моментом, о котором осталось вспомнить, является наличие в сборке .NET и таких метаданных, которые описывают саму сборку (они формально называются манифестом). Помимо прочих деталей, в манифесте документируются все внешние сборки, которые требуются текущей сборке для корректного функционирования, версия сборки, информация об авторских правах и т.д. Как и за генерацию метаданных типов, за генерацию манифеста сборки тоже всегда отвечает компилятор. Ниже приведены некоторые наиболее существенные детали манифеста, сгенерированного в результате компиляции приведенного ранее в этой главе файла двоичного кода Calc.cs (здесь предполагается, что компилятору было указано назначить сборке имя Calc . ехе): .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) .ver 4:0:0:0 } .assembly Calc { .hash algorithm 0x00008004 .ver 1:0:0:0 } .module Calc.exe .imagebase 0x00400000 .subsystem 0x00000003 .file alignment 512 .corflags 0x00000001 В двух словах, в этом манифесте представлен список требуемых для Calc. ехе внешних сборок (в директиве . assembly extern), а также различные характеристики самой сборки (наподобие номера версии, имени модуля и т.д.). О пользе данных манифеста будет гораздо более подробно рассказываться в главе 14. Что собой представляет общая система типов (CTS) В каждой конкретной сборке может содержаться любое количество различающихся типов. В мире .NET "тип" представляет собой просто общий термин, который применяется для обозначения любого элемента из множества (класс, интерфейс, структура, перечисление, делегат}. При построении решений с помощью любого языка .NET, скорее всего, придется взаимодействовать со многими из этих типов. Например, в сборке
Глава 1. Философия .NET 61 может содержаться один класс, реализующий определенное количество интерфейсов, метод одного из которых может принимать в качестве входного параметра перечисление, а возвращать структуру. Вспомните, что CTS (общая система типов) представляет собой формальную спецификацию, в которой описано то, как должны быть определены типы для того, чтобы они могли обслуживаться в CLR-среде. Внутренние детали CTS обычно интересуют только тех, кто занимается разработкой инструментов и/или компиляторов для платформы .NET. Абсолютно всем .NET-программистам, однако, важно уметь работать на предпочитаемом ими языке с пятью типами из CTS. Краткий обзор этих типов приведен ниже. Типы классов В каждом совместимом с .NET языке поддерживается, как минимум, понятие типа класса (class type), которое играет центральную роль в объектно-ориентированном программировании (ООП). Каждый класс может включать в себя любое количество членов (таких как конструкторы, свойства, методы и события) и точек данных (полей). В С# классы объявляются с помощью ключевого слова class. // Тип класса С# с одним методом. class Calc { public int Add(int x, int y) { return x + y; } } В главе 5 будет более подробно показываться, как можно создавать типы классов CTS в С#, а пока в табл. 1.1 приведен краткий перечень характеристик, которые свойственны типам классов. Таблица 1.1. Характеристики классов CTS Характеристика 0пи классов Запечатанные Запечатанные (sealed), или герметизированные, классы не могут выступать в роли базовых для других классов, т.е. не допускают наследования Реализующие Интерфейсом (interface) называется коллекция абстрактных членов, кото- интерфейсы рые обеспечивают возможность взаимодействия между объектом и пользователем этого объекта. CTS позволяет реализовать в классе любое количество интерфейсов Абстрактные Экземпляры абстрактных (abstract) классов не могут создаваться напря- или мую, и предназначены для определения общих аспектов поведения для конкретные производных типов. Экземпляры же конкретных (concrete) классы могут создаваться напрямую Степень видимости Каждый класс должен конфигурироваться с атрибутом видимости (visibility). По сути, этот атрибут указывает, должен ли класс быть доступным для использования внешним сборкам или только изнутри определяющей сборки Типы интерфейсов Интерфейсы представляют собой не более чем просто именованную коллекцию определений абстрактных членов, которые могут поддерживаться (т.е. реализоваться) в данном классе или структуре. В С# типы интерфейсов определяются с помощью ключевого слова interface, как, например, показано ниже:
62 Часть I. Общие сведения о языке С# и платформе .NET // Тип интерфейса в С# обычно объявляется // общедоступным, чтобы позволить типам в других // сборках реализовать его поведение. public interface IDraw { void Draw(); } Сами по себе интерфейсы мало чем полезны. Однако когда они реализуются в классах или структурах уникальным образом, они позволяют получать доступ к дополнительным функциональным возможностям за счет добавления просто ссылки на них в полиморфной форме. Тема программирования с использованием интерфейсов подробно рассматривается в главе 9. Типы структур Понятие структуры тоже сформулировано в CTS. Тем, кому приходилось работать с языком С, будет приятно узнать, что таким пользовательским типам удалось "выжить" в мире .NET (хотя на внутреннем уровне они и ведут себя несколько иначе). Попросту говоря, структура может считаться "облегченным" типом класса с основанной на использовании значений семантикой. Более подробно об особенностях структур будет рассказываться в главе 4. Обычно структуры лучше всего подходят для моделирования геометрических и математических данных, и в С# они создаются с помощью ключевого слова struct. / / Тип структуры в С#. struct Point i //В структурах могут содержаться поля. public int xPos, yPos; //В структурах могут содержаться параметризованные конструкторы. public Point (int x, int у) { xPos = x; yPos = y; } // В структурах могут определяться методы. public void PrintPosition () { Console.WriteLine (" ({ 0}, {1})", xPos, yPos); } } Типы перечислений Перечисления (enumeration) представляют собой удобную программную конструкцию, которая позволяет группировать данные в пары "имя-значение". Например, предположим, что требуется создать приложение видеоигры, в котором игроку бы позволялось выбирать персонажа одной из трех следующих категорий: Wizard (маг), Fighter (воин) или Thief (вор). Вместо того чтобы использовать и отслеживать числовые значения для каждого варианта, в этом случае гораздо удобнее создать соответствующее перечисление с помощью ключевого слова enum: // Тип перечисления С#. public enum CharacterType { Wizard = 100, Fighter = 200, Thief = 300 }
Глава 1. Философия .NET 63 По умолчанию для хранения каждого элемента выделяется блок памяти, соответствующий 32-битному целому, однако при необходимости (например, при программировании с расчетом на устройства, обладающие малыми объемами памяти, вроде мобильных устройств Windows) это значение можно изменить. Кроме того, в CTS необходимо, чтобы перечислймые типы наследовались от общего базового класса System.Enum. Как будет показано в главе 4, в этом базовом классе присутствует ряд весьма интересных членов, которые позволяют извлекать, манипулировать и преобразовывать базовые пары "имя-значение" программным образом. Типы делегатов Делегаты (delegate) являются .NET-эквивалентом безопасных в отношении типов указателей функций в стиле С. Главное отличие заключается в том, что делегат в .NET представляет собой класс, который наследуется от System.MulticastDelegate, a не просто указатель на какой-то конкретный адрес в памяти. В С# делегаты объявляются с помощью ключевого слова delegate. // Этот тип делегата в С# может 'указывать' на любой метод, // возвращающий целое число и принимающий два целых // числа в качестве входных данных. public delegate int BinaryOp(int x, int y); Делегаты очень удобны, когда требуется обеспечить одну сущность возможностью перенаправлять вызов другой сущности и образовывать основу для архитектуры обработки событий .NET. Как будет показано в главах 11 и 19, делегаты обладают внутренней поддержкой для групповой адресации (т.е. пересылки запроса сразу множеству получателей) и асинхронного вызова методов (т.е. вызова методов во вторичном потоке). Члены типов Теперь, когда было приведено краткое описание каждого из сформулированных в CTS типов, пришла пора рассказать о том, что большинство из этих типов способно принимать любое количество членов (member). Формально в роли члена типа может выступать любой элемент из множества {конструктор, финализатор, статический конструктор, вложенный тип, операция, метод, свойство, индексатор, поле, поле только для чтения, константа, событие). В спецификации CTS описываются различные "характеристики", которые могут быть ассоциированы с любым членом. Например, каждый член может обладать характеристикой, отражающей его доступность (т.е. общедоступный, приватный или защищенный). Некоторые члены могут объявляться как абстрактные (для навязывания полиморфного поведения производным типам) или как виртуальные (для определения фиксированной, но допускающей переопределение реализации). Кроме того, почти все члены также могут делаться статическими членами (привязываться на уровне класса) или членами экземпляра (привязываться на уровне объекта). Более подробно о том, как можно создавать членов, будет рассказываться в ходе нескольких следующих глав. На заметку! Как будет описано в главе 10, в языке С# также поддерживается создание обобщенных типов и членов. Встроенные типы данных И, наконец, последним, что следует знать о спецификации CTS, является то, что в ней содержится четко определенный набор фундаментальных типов данных. Хотя в каждом отдельно взятом языке для объявления того или иного встроенного типа данных
64 Часть I. Общие сведения о языке С# и платформе .NET из CTS обычно предусмотрено свое уникальное ключевое слово, все эти ключевые слова в конечном итоге соответствуют одному и тому же типу в сборке mscorlib. dll. В табл. 1.2 показано, как ключевые типы данных из CTS представляются в различных .NET-языках. Таблица 1.2. Встроенные типы данных, описанные в CTS Тип данных в CTS Ключевое слово в Visual Basic Ключевое слово в С# Ключевое слово в С++и CLI System.ByteByte System.SByteSByte System.Intl6 System.Int32 System.Int64 System.UIntl6 System.UInt32 System.UInt64 System.SingleSingle System.DoubleDouble System.Ob]ectObject System.CharChar System.StringString System.DecimalDecimal System.BooleanBoolean Byte SByte Short Integer Long UShort Ulnteger ULong Single Double Object Char String Decimal Boolean byte sbyte short int long ushort uint ulong float double object char String decimal bool unsigned char signed char short int или long int64 unsigned unsigned unsigned unsigned float double objectA wchar t StringA Decimal bool short int или long int64 Из-за того факта, что уникальные ключевые слова в любом управляемом языке являются просто сокращенными обозначениями реального типа из пространства имен System, больше не нужно беспокоиться ни об условиях переполнения и потери значимости (overflow/underflow) в случае числовых данных, ни о внутреннем представлении строк и булевских значений в различных языках. Рассмотрим следующие фрагменты кода, в которых 32-битные числовые переменные определяются в С# и Visual Basic с использованием соответствующих ключевых слов из самих языков, а также формального типа из CTS: // Определение числовых переменных в С#. int 1=0; System.Int32 j = 0; 1 Определение числовых переменных в VB. Dim 1 As Integer = 0 Dim j As System.Int32 = 0 Что собой представляет общеязыковая спецификация (CLS) Как известно, в разных языках программирования одни и те же программные конструкции выражаются своим уникальным, специфическим для конкретного языка образом. Например, в С# конкатенация строк обозначается с помощью знака "плюс" (+),
Глава 1. Философия .NET 65 а в VB для этого обычно используется амперсанд (&). Даже в случае выражения в двух отличных языках одной и той же программной идиомы (например, функции, не возвращающей значения), очень высока вероятность того, что с виду синтаксис будет выглядеть очень по-разному: //Не возвращающий ничего метод в С#. public void MyMethod () { // Какой-нибудь интересный код... } ' Не возвращающий ничего метод в VB. Public Sub MyMethod () ' Какой-нибудь интересный код... End Sub Как уже показывалось, подобные небольшие вариации в синтаксисе для исполняющей среды .NET являются несущественными благодаря тому, что соответствующие компиляторы (в данном случае — csc.exe и vbc.exe) генерируют схожий набор CIL- инструкций. Однако языки могут еще отличаться и по общему уровню функциональных возможностей. Например, в каком-то из языков .NET может быть или не быть ключевого слова для представления данных без знака, а также поддерживаться или не поддерживаться типы указателей. Из-за всех таких вот возможных вариаций было бы просто замечательно иметь в распоряжении какие-то опорные требования, которым должны были бы отвечать все поддерживающие .NET языки. CLS (Common Language Specification — общая спецификация для языков программирования) как раз и представляет собой набор правил, которые во всех подробностях описывают минимальный и полный комплект функциональных возможностей, которые должен обязательно поддерживать каждый отдельно взятый .NET-компилятор для того, чтобы генерировать такой программный код, который мог бы обслуживаться CLR и к которому в то же время могли бы единообразным образом получать доступ все языки, ориентированные на платформу .NET. Во многих отношениях CLS может считаться просто подмножеством всех функциональных возможностей, определенных в CTS. В конечном итоге CLS является своего рода набором правил, которых должны придерживаться создатели компиляторов при желании, чтобы их продукты могли без проблем функционировать в мире .NET Каждое из этих правил имеет простое название (например, "Правило CLS номер 6") и описывает, каким образом его действие касается тех, кто создает компиляторы, и тех, кто (каким-либо образом) будет взаимодействовать с ними. Самым главным в CLS является правило J, гласящее, что правила CLS касаются только тех частей типа, которые делаются доступными за пределами сборки, в которой они определены. Из этого правила можно (и нужно) сделать вывод о том, что все остальные правила в CLS не распространяются на логику, применяемую для построения внутренних рабочих деталей типа .NET. Единственными аспектами типа, которые должны соответствовать CLS, являются сами определения членов (т.е. соглашения об именовании, параметры и возвращаемые типы). В рамках логики реализации члена может применяться любое количество и не согласованных с CLS приемов, поскольку для внешнего мира это не будет играть никакой роли. Для иллюстрации ниже приведен метод Add () на языке С#, который не отвечает правилам CLS, поскольку в его параметрах и возвращаемых значениях используются данные без знака (что не является требованием CLS): class Calc I // Использование данных без знака внешним образом // не соответствует правилам CLS!
66 Часть I. Общие сведения о языке С# и платформе .NET public ulong Add(ulong x, ulong y) { return x + y; } } Однако если бы мы просто использовали данные без знака внутренним образом, как показано ниже: class Calc { public int Adddnt x, mt y) { // Поскольку переменная ulong используется здесь // только внутренне, правила CLS не нарушаются. ulong temp = 0; return x + у; } } тогда правила CLS были бы соблюдены и все языки .NET могли бы обращаться к данному методу Add (). Разумеется, помимо правила 1 в CLS содержится и много других правил. Например, в CLS также описано, каким образом в каждом конкретном языке должны представляться строки текста, оформляться перечисления (подразумевающие использование базового типа для хранения), определяться статические члены и т.д. К счастью, для того, чтобы быть умелым разработчиком .NET, запоминать все эти правила вовсе не обязательно. Опять-таки, очень хорошо разбираться в спецификациях CTS и CLS необходимо только создателям инструментов и компиляторов. Забота о соответствии правилам CLS Как можно будет увидеть в ходе прочтения настоящей книги, в С# на самом деле имеется ряд программных конструкций, которые не соответствуют правилам CLS. Хорошая новость, однако, состоит в том, что компилятор С# можно заставить выполнять проверку программного кода на предмет соответствия правилам CLS с помощью всего лишь единственного атрибута. NET: / / Указание компилятору С# выполнять проверку //на предмет соответствия CLS. [assembly: System.CLSCompliant(true)] Детали программирования с использованием атрибутов более подробно рассматриваются в главе 15. А пока главное понять просто то, что атрибут [CLSCompliant] заставляет компилятор С# проверять каждую строку кода на предмет соответствия правилам CLS. В случае обнаружения нарушения каких-нибудь правил CLS компилятор будет выдавать ошибку и описание вызвавшего ее кода. Что собой представляет общеязыковая исполняющая среда (CLR) Помимо спецификаций CTS и CLS, для получения общей картины на данный момент осталось рассмотреть еще одну аббревиатуру — CLR, которая расшифровывается как Common Language Runtime (общеязыковая исполняющая среда). С точки зрения программирования под термином исполняющая среда может пониматься коллекция внешних служб, которые требуются для выполнения скомпилированной единицы программного кода. Например, при использовании платформы MFC для создания нового
Глава 1. Философия .NET 67 приложения разработчики осознают, что их программе требуется библиотека времени выполнения MFC (т.е. mfc42 . dll). Другие популярные языки тоже имеют свою исполняющую среду: программисты, использующие язык VB6, к примеру, вынуждены привязываться к одному или двум модулям исполняющей среды (вроде msvbvm60 .dll), a разработчики на Java — к виртуальной машине Java (JVM). В составе .NET предлагается еще одна исполняющая среда. Главное отличие между исполняющей средой .NET и упомянутыми выше средами, состоит в том, что исполняющая среда .NET обеспечивает единый четко определенный уровень выполнения, который способны использовать все совместимые с .NET языки и платформы. Основной механизм CLR физически имеет вид библиотеки под названием mscoree .dll (и также называется общим механизмом выполнения исполняемого кода объектов — Common Object Runtime Execution Engine). При добавлении ссылки на сборку для ее использования загрузка библиотеки mscoree . dll осуществляется автоматически и затем, в свою очередь, приводит к загрузке требуемой сборки в память. Механизм исполняющей среды отвечает за выполнение целого ряда задач. Сначала, что наиболее важно, он отвечает за определение места расположения сборки и обнаружение запрашиваемого типа в двоичном файле за счет считывания содержащихся там метаданных. Затем он размещает тип в памяти, преобразует CIL-код в соответствующие платформе инструкции, производит любые необходимые проверки на предмет безопасности и после этого, наконец, непосредственно выполняет сам запрашиваемый программный код. Помимо загрузки пользовательских сборок и создания пользовательских типов, механизм CLR при необходимости будет взаимодействовать и с типами, содержащимися в библиотеках базовых классов .NET. Хотя вся библиотека базовых классов поделена на ряд отдельных сборок, главной среди них является сборка ms cor lib .dll. В этой сборке содержится большое количество базовых типов, охватывающих широкий спектр типичных задач программирования, а также базовых типов данных, применяемых во всех языках .NET. При построении .NET-решений доступ к этой конкретной сборке будет предоставляться автоматически. На рис. 1.4 схематично показано, как выглядят взаимоотношения между исходным кодом (предусматривающим использование типов из библиотеки базовых классов), компилятором .NET и механизмом выполнения .NET. Различия между сборками, пространствами имен и типами Каждый из нас понимает важность библиотек программного кода. Главная цель таких библиотек, как MFC, Java Enterprise Edition или ATL, заключается в предоставлении разработчикам набора готовых, правильно оформленных блоков программного кода, чтобы они могли использовать их своих приложениях. Язык С#, однако, не поставляется с какой-либо специфической библиотекой кода. Вместо этого от разработчиков, использующих С#, требуется применять нейтральные к языкам библиотеки, которые поставляются в .NET. Для поддержания всех типов в библиотеках базовых классов в хорошо организованном виде в .NET широко применяется понятие пространства имен (namespace). Под пространством имен понимается группа связанных между собой с семантической точки зрения типов, которые содержатся в сборке. Например, в пространстве имен System. 10 содержатся типы, имеющие отношение к операциям ввода-вывода, в пространстве имен System. Data — основные типы для работы с базами данных, и т.д.
68 Часть I. Общие сведения о языке С# и платформе .NET Исходный код .NET несовместимом с .NET языке Механизм выполнения .NET (mscoree.dll) Загрузчик классов ЛТ-компилятор г Соответствующие платформе инструкции ■ ■ Выполнение члена Рис. 1.4. Механизм mscoree.dll в действии Очень важно понимать, что в одной сборке (например, ms cor lib. dll) может содержаться любое количество пространств имен, каждое из которых, в свою очередь, может иметь любое число типов. Чтобы стало понятнее, на рис. 1.5 показан снимок окна предлагаемой в Visual Studio 2010 утилиты Object Browser. Эта утилита позволяет просматривать сборки, на которые имеются ссылки в текущем проекте, пространства имен, содержащиеся в каждой из этих сборок, типы, определенные в каждом из этих пространств имени, и члены каждого из этих типов. Важно обратить внимание на то, что в mscorlib.dll содержится очень много самых разных пространств имен (вроде System. 10), и что в каждом из них содержатся свои семантически связанные типы (такие как BinaryReader) . Компилятор .NET Сборка * .dll или *.ехе (с CIL-инструкциями, метаданными и манифестом) Библиотеки базовых классов (mscorlib.dll и др.)
Глава 1. Философия .NET 69 Object Browse/ X Q Browse: My Solution • ... |<Search> - J | ! j3 CSharpAdder i> >J Microsoft.CSharp {} Microsoft.Win32 {} Microsoft.Win32.SafeHandies i {) System.Collections • {} System.Collections.Concurrent {} System.Collections.Generic {} System.Collectiora.ObjectModel {} System.Configuratton.Assemblie5 ; {} System.Deployment.Internal {} System.Diagnostics t> {} System.Diagnostics.CodeAnarysis > {} System.Diagnostics.Contracts 0 System.Diagnostics.Eventing > {} System.Diagnostics.SymbolStore О System.Globalization л {} System.IO ■*jU! > -*J BinaryWrrter «tj BufferedStream 4 BinaryReader(SystemJO.Stream, System.Text.Encoding) -» ♦ BinaryReader(SystemJO.Stream) ♦ CloseO Ф DisposeO y* Dispose(bool) v FillBuffer(mt) ♦ PeekCharQ * Read(byte(L int int) # Read(char[L int int) * ReadO ft Read7BitEncodedIntO ♦ ReedBooleanO * ReadByteO Ф ReadBytes(rnt) V ReadCharO * ReadChars(int) ♦ BadBwrwM V ReadDoubleQ Уш1шш&ЬАЯй4ШШй1штшжтйш тиггпшчгггигпгиипгп-пппппт! и -11 [public class Binary Reader 1 Member of System .10 1 Summary: 1 Reads primitive data types as binary values in a specific encoding. Рис. 1.5. В одной сборке может содержаться произвольное количество пространств имен Птавное отличие между таким подходом и зависящими от конкретного языка библиотеками вроде MFC состоит в том, что он обеспечивает использование во всех языках, ориентированных на среду выполнения .NET, одних и тех лее пространств имен и одних и тех лее типов. Например, в трех приведенных ниже программах иллюстрируется создание постоянно применяемого примера "Hello World" на языках С#, VB и C++/CLI. // Hello world на языке С# using System; public class MyApp { static void Mam() { Console.WriteLine ("Hi from C#"); ' Hello world на языке VB Imports System Public Module MyApp Sub Main () Console.WriteLine ("Hi from VB") End Sub End Module // Hello world на языке C++/CLI #include "stdafx.h" using namespace System; int main(array<System::String Л> Aargs) { Console::WriteLine("Hi from C++/CLI"), return 0; Обратите внимание, что в каждом из языков применяется класс Console, определенный в пространстве имен System. Если отбросить незначительные синтаксические отличия, то в целом все три программы выглядят очень похоже, как по форме, так и по логике.
70 Часть I. Общие сведения о языке С# и платформе .NET Очевидно, что главной задачей любого планирующего использовать .NET разработчика является освоение того обилия типов, которые содержатся в (многочисленных) пространствах имен .NET. Самым главным пространством имен, с которого следует начинать, является System. В этом пространстве имен содержится набор ключевых типов, которые любому разработчику .NET нужно будет эксплуатировать снова и снова. Фактически создание функционального приложения на С# невозможно без добавления хотя бы ссылки на пространство имен System, поскольку все главные типы данных (вроде System. Int32, System. String и т.д.) содержатся именно здесь. В табл. 1.3 приведен краткий список некоторых (но, конечно же, не всех) предлагаемых в .NET пространств имен, которые были поделены на группы на основе функциональности. Таблица 1.3. Некоторые пространства имен в .NET Пространство имен в .NET Описание System System.Collections System.Collections.Generic System.Data System.Data.Common System.Data.EntityClient System.Data.SqlClient System.10 System.10.Compression System.10.Ports System.Reflection System.Refleetion.Emit System.Runtime.InteropServices System.Drawing System.Windows.Forms System.Windows System.Windows.Controls System.Windows.Shapes System.Linq System.Xml.Linq System.Data.DataSetExtensions System.Web Внутри пространства имен System содержится множество полезных типов, позволяющих иметь дело с внутренними данными, математическими вычислениями, генерированием случайных чисел, переменными среды и сборкой мусора, а также ряд наиболее часто применяемых исключений и атрибутов В этих пространствах имен содержится ряд контейнерных типов, а также несколько базовых типов и интерфейсов, которые позволяют создавать специальные коллекции Эти пространства имен применяются для взаимодействия с базами данных с помощью AD0.NET В этих пространствах содержится много типов, предназначенных для работы с операциями файлового ввода- вывода, сжатия данных и манипулирования портами В этих пространствах имен содержатся типы, которые поддерживают обнаружение типов во время выполнения, а также динамическое создание типов В этом пространстве имен содержатся средства, с помощью которых можно позволить типам .NET взаимодействовать с "неуправляемым кодом" (например, DLL- библиотеками на базе С и серверами СОМ) и наоборот В этих пространствах имен содержатся типы, применяемые для построения настольных приложений с использованием исходного набора графических инструментов .NET (Windows Forms) Пространство System.Windows является корневым среди этих нескольких пространств имен, которые представляют собой набор графических инструментов Windows Presentation Foundation (WPF) В этих пространствах имен содержатся типы, применяемые при выполнении программирования с использованием API-интерфейса LINQ Это пространство имен является одним из многих, которые позволяют создавать веб-приложения ASP.NET
Глава 1. Философия .NET 71 Окончание табл. 1.3 Пространство имен в .NET Описание System. ServiceModel Это пространство имен является одним из многих, которые позволяется применять для создания распределенных приложений с помощью API-интерфейса Windows Communication Foundation (WCF) System. Workflow. Runtime Эти два пространства имен являются главными предста- System. Workflow.Activities вителями многочисленных пространств имен, в которых содержатся типы, применяемые для построения поддерживающих рабочие потоки приложений с помощью API-интерфейса Windows Workflow Foundation (WWF) System.Threading В этом пространстве имен содержатся многочисленные System.Threading. Tasks типы для построения многопоточных приложений, способных распределять рабочую нагрузку среди нескольких ЦП. System. Security Безопасность является неотъемлемым свойством мира .NET. В относящихся к безопасности пространствах имен содержится множество типов, которые позволяют иметь дело с разрешениями, криптографической защитой и т.д System. Xml В этом ориентированном на XML пространстве имен содержатся многочисленные типы, которые можно применять для взаимодействия с XML-данными Роль корневого пространства Microsoft При изучении перечня, приведенного в табл. 1.3, нетрудно было заметить, что пространство имен System является корневым для приличного количества вложенных пространств имен (таких как System. 10, System. Data и т.д.). Как оказывается, однако, помимо System в библиотеке базовых классов предлагается еще и ряд других корневых пространств имен наивысшего уровня, наиболее полезным из которых является пространство имен Microsoft. В любом пространстве имен, которое находится внутри пространства имен Microsoft (как, например, Microsoft.CSharp, Microsoft.ManagementConsole и Microsoft .Win32), содержатся типы, применяемые для взаимодействия исключительно с теми службами, которые свойственны только лишь операционной системе Windows. Из-за этого не следует предполагать, что данные типы могут с тем же успехом применяться и в других поддерживающих .NET операционных системах вроде Мае OS X. В настоящей книге детали вложенных в Microsoft пространств имен подробно рассматриваться не будут, поэтому заинтересованным придется обратиться к документации .NET Framework 4.0 SDK. На заметку! В главе 2 будет показано, как пользоваться документацией .NET Framework 4.0 SDK, в которой содержатся детальные описания всех пространств имен, типов и членов, встречающихся в библиотеках базовых классов. Получение доступа к пространствам имен программным образом Не помешает снова вспомнить, что пространства имен представляют собой не более чем удобный способ логической организации взаимосвязанных типов для упрощения работы с ними. Давайте еще раз обратимся к пространству имен System. С человеческой точки зрения System. Console представляет класс по имени Console, который
72 Часть I. Общие сведения о языке С# и платформе .NET содержится внутри пространства имен под названием System. Но с точки зрения исполняющей .NET это не так. Механизм исполняющей среды видит только одну лишь сущность по имени System. Console. В С# ключевое слово using упрощает процесс добавления ссылок на типы, содержащиеся в определенном пространстве имен. Вот как оно работает. Предположим, что требуется создать графическое настольное приложение с использованием API-интерфейса Windows Forms. В главном окне этого приложения должна визуализироваться гистограмма с информацией, получаемой из базы данных, и отображаться логотип компании. Поскольку для изучения типов в каждом пространстве имен требуются время и силы, ниже показаны некоторые из возможных кандидатов на использование в такой программе: // Все пространства имен, которые можно использовать // для создания подобного приложения. using System; // Общие типы из библиотеки базовых классов. using System.Drawing; // Типы для визуализации графики. using System.Windows.Forms; // Типы для создания элементов пользовательского // интерфейса с помощью Windows Forms, using System.Data; // Общие типы для работы с данными, using System. Data. SqlClient; // Типы для доступа к данным MS SQL Server. После указания ряда необходимых пространств имен (и добавления ссылки на сборки, в которых они находятся), можно свободно создавать экземпляры типов, которые в них содержатся. Например, при желании создать экземпляр класса Bitmap (определенного в пространстве имен System. Drawing), можно написать следующий код: // Перечисляем используемые в данном файле // пространства имен явным образом. using System; using System.Drawing; class Program { public void DisplayLogo () { // Создаем растровое изображение // размером 20*20 пикселей. Bitmap companyLogo = new BitmapB0, 20); } } Благодаря импортированию в этом коде пространства имен System. Drawing, компилятор сможет определить, что класс Bitmap является членом данного пространства имен. Если пространство имен System. Drawing не указать, компилятор сообщит об ошибке. При желании переменные также можно объявлять с использованием полностью уточненного имени: II Пространство имен System.Drawing здесь не указано! using System; class Program { public void DisplayLogo () { // Используем полностью уточненное имя. System.Drawing.Bitmap companyLogo = new System.Drawing.BitmapB0, 20); } }
Глава 1. Философия .NET 73 Хотя определение типа с использованием полностью уточненного имени позволяет делать код более удобным для восприятия, трудно не согласиться с тем, что применение поддерживаемого в С# ключевого слова using, в свою очередь, позволяет значительно сократить количество печатаемых знаков. Поэтому в настоящей книге мы будем стараться избегать использования полностью уточненных имен (если только не будет возникать необходимости в устранении какой-то очевидной неоднозначности) и стремиться пользоваться более простым подходом, т.е. ключевым словом using. Важно помнить о том, что ключевое слово using является просто сокращенным способом указания полностью уточненного имени. Поэтому любой из этих подходов приводит к получению одного и того же CIL-кода (с учетом того факта, что в CIL-коде всегда применяются полностью уточненные имена) и не сказывается ни на производительности, ни на размере сборки. Добавление ссылок на внешние сборки Помимо указания пространства имен с помощью поддерживаемого в С# ключевого слова using, компилятору С# необходимо сообщить имя сборки, в которой содержится само CIL-onpeделение упоминаемого типа. Как уже отмечалось, многие из ключевых пространств имен .NET находятся внутри сборки mscorlib.dll. Класс System. Drawing. Bitmap, однако, содержится в отдельной сборке по имени System. Drawing, dll. Подавляющее большинство сборок в .NET Framework размещено в специально предназначенном для этого каталоге, который называется глобальным кэшем сборок (Global Assembly Cache — GAC). На машине Windows по умолчанию GAC может располагаться внутри каталога ?Qwindir%\Assembly, как показано на рис. 1.6. VT Favorite ■ Desktop Ц] Recent Places Л Libraries 0 Documents J* Music W Pictures Щ Videos «$ Homegroup '■ Computer f» Mongo Drive (C:) ^ CD Dnve (F:) Assembly Name j JUSystem.Data.SqlXml j sASystem.Deployment jfO System.Design db System.DirectoryServices i£i System. DirectoryServices-AccountManagement 3.5.0.0 #Ь System.DirectoryServtces.Protocois j iASyst em.Drawing.Design ж) System.EnterpriseServices 40 System.EnterpriseServices I lASystem.IdentityModel ЯХ System.ldentityModel.Selectors ifl System JO.Log dll System.Management Ж1 System. Management-Automatior Version Cut.., Public Key Token 2.0.0.0 2.0.0.0 20.0.0 2.0.0.0 3.5.0.0 2.0.0.0 2.0.0.0 2.0.0.0 2.0.0.0 2.0.0,0 5.0.0.0 3.0,0.0 3.0.0,0 2.0 0.0 1.0.0.0 Ь77а5с561934е089 b036f7flld50a3a b03f5f7flld50a3a b03f5f7flld50a3a Ь77а5с5б1934е089 b03f5f7flld50a3a b03f5f7flld50a3a b03f5f7flld50a3a b03f5f7flld50a3a b03f5f7flld50a3a Ь77а5с561934е089 Ь77а5с561934е089 b03f5r7flld50a3a b03f5f7flld50a3a 31bf3856ad364e35 Рис. 1.6. Многие библиотеки .NET размещены в GAC В зависимости от того, какое средство применяется для разработки приложений .NET, на выбор может оказываться доступными несколько различных способов для уведомления компилятора о том, какие сборки требуется включить в цикл компиляции. Эти способы подробно рассматриваются в следующей главе, а здесь их детали опущены. На заметку! С выходом версии .NET 4.0 в Microsoft решили выделить под сборки .NET 4.0 специальное место, находящееся отдельно от каталога С : \Windows\Assembly. Более подробно об этом будет рассказываться в главе 14.
74 Часть I. Общие сведения о языке С# и платформе .NET Изучение сборки с помощью утилиты ildasm. exe Тем, кого начинает беспокоить мысль о необходимости освоения всех пространств имен в .NET, следует просто вспомнить о том, что уникальным пространство имен делает то, что в нем содержатся типы, которые как-то связаны между собой с семантической точки зрения. Следовательно, если потребность в создании пользовательского интерфейса, более сложного, чем у простого консольного приложения, отсутствует, можно смело забыть о таких пространствах имен, как System. Windows . Forms, System.Windows и System.Web (и ряда других), а при создании приложений для рисования — о пространствах имен, которые касаются работы с базами данных. Как и в случае любого нового набора уже готового кода, опыт приходит с практикой. Утилита ildasm.exe (Intermediate Language Disassembler — дизассемблер промежуточного языка), которая поставляется в составе пакета .NET Framework 4.0 SDK, позволяет загружать любую сборку .NET и изучать ее содержимое, в том числе ассоциируемый с ней манифест, CIL-код и метаданные типов. По умолчанию эта утилита установлена в каталоге С: \Program Files\Microsoft SDKs\Windows\v7 . 0A\bin (если здесь ее нет, поищите на компьютере файл по имени ildasm.exe). На заметку! Утилиту ildasm.exe легко запустить, открыв в Visual Studio 2010 окно Command Prompt (Командная строка), введя в нем слово ildasm и нажав клавишу <Enter>. Р Calcexe-ILDASM Btc View Help la yy ЕЯ ► MANIFEST й Щ CakulatorExample f JE CakulatorExample.Calc ► .class public auto ansi beforefieUin* !•■ ■ .ctor: votd() ; ■ Add : int32(int32,int32) й £ Calculator Example. Program ► .class public auto ansi beforefieldinit ■ .ctor: voJd() U Main : void() После запуска этой утилиты нужно выбрать в меню File (Файл) команду Open (Открыть) и найти сборку, которую требуется изучить. Для целей иллюстрации здесь предполагается, что нужно изучить сборку Calc. exe, которая была сгенерирована на основе приведенного ранее в этой главе файла Calc. сs (рис. 1.7). Утилита ildasm.exe представляет структуру любой сборки в знакомом древовидном формате. Просмотр CIL-кода Рис. 1.7. Утилита ildasm.exe позволяет просматривать содержащийся внутри сборки .NET CIL-код, манифест и метаданные типов Помимо содержащихся в сборке пространств имен, типов и членов, утилита ildasm. exe также позволяет просматривать и CIL-инструкции, которые лежат в основе каждого конкретного члена. Например, в результате двойного щелчка на методе Main () в классе Program открывается отдельное окно с CIL-кодом, лежащим в основе этого метода, как показано на рис. 1.8. Просмотр метаданных типов Для просмотра метаданных типов, которые содержатся в загруженной в текущий момент сборке, необходимо нажать комбинацию клавиш <Ctrl+M>. На рис. 1.9 показаны метаданные метода Calc. Add (). Просмотр метаданных сборки (манифеста) И, наконец, чтобы просмотреть содержимое манифеста сборки, необходимо дважды щелкнуть на значке MANIFEST (рис. 1.10).
Глава 1. Философия .NET 75 f? Cakulatorbcampte.CalcApp~Main: voidQ J ( Find Find Ned .method priuate hidebysig static uoid Main() cil managed .entrypoint | // Code size Л2 <0x2a) И .maxstack 3 \ .locals init (class CalculatorExample.Calc и 0, int32 U 1) J IL ••••: nop ] IL 0001: newobj instance uoid CalculatorExample.Calc:: Щ IL tOM: stloc.i U IL 0007: ldloc.O J IL 0000: Idc.iij.s 10 J IL 000a: ldc.iU.s 84 1 IL 000c: calluirt instance int32 CalculatorExample.Calc: [ IL_0011: stloc.1 1" 1IS[ ШЗШ ^ pj 1 .ctor() 1 :Add(int32, int32) w\ Рис. 1.8. Просмотр лежащего в основе CIL-кода /7" Metalnfo ьь'щйш Method 01 @6000003) HethodName Flags RUfl ImplFlags CallCnuntn hasThis ReturnType 2 Arguments Argument 01: Argument 02: 2 Parameters A) ParamToken B) ParamToken Method 02 @6000004) Add @6000003) [Public] [HideBySig] [ReuseSlot] 0x00002090 [IL] [Managed] @0000000) [DEFAULT] 14 H @8000001) Mame : x Flags: [none] @0000 @8000002) Name : у Flags: [none] @0000 .ctor @6000004) Рис. 1.9. Просмотр метаданных типов с помощью ildasm.exe /7 MANIFEST Find Find Next // Metadata version: u4.0.3 0128 assembly extern mscorlib < i=| .publickeytoken - (B7 7A 5C 56 19 34 EO 89 ) .ver 4:8:0:0 — } .assembly Calc < .custom instance uoid [mscorlib]System.Runtime.CompilerSeruice< .custom instance uoid [mscorlib]System.Runtime.CompilerSeruice* - Рис. 1.10. Просмотр данных манифеста с помощью ildasm.exe Несомненно, утилита ildasm. ехе обладает большим, чем было показано здесь количеством функциональных возможностей; все остальные функциональные возможности этой утилиты будут демонстрироваться позже в книге при рассмотрении соответствующих аспектов. Изучение сборки с помощью утилиты Reflector Хотя утилита ildasm.exe и применяется очень часто для просмотра деталей двоичного файла .NET, одним из ее недостатков является то, что она позволяет просматривать только лежащий в основе CIL-код, но не реализацию сборки с использованием
76 Часть I. Общие сведения о языке С# и платформе .NET предпочитаемого управляемого языка. К счастью, в Интернете для загрузки доступно множество других утилит для просмотра и декомпиляции объектов .NET, в том числе и популярная утилита Reflector. Эта утилита распространяется бесплатно и доступна по адресу http://www. red-gate.com/products/reflector. После распаковки из ZIP-архива ее можно запускать и подключать к любой представляющей интерес сборке, выбирая в меню File (Файл) команду Open (Открыть). На рис. 1.11 показано ее применение на примере приложения Calc.exe. Рис. 1.11. Утилита Reflector является очень популярной программой для просмотра объектов Важно обратить внимание на то, что в утилите reflector.exe поддерживается окно Dissembler (Дизассемблер), которое можно открыть нажатием клавиши пробела, а также элемент раскрывающегося списка, который позволяет просматривать лежащую в основе кодовую базу на желаемом языке (разумеется, в том числе и на CIL). Остальные интригующие функциональные возможности этой утилиты предлагается изучить самостоятельно. На заметку! Следует иметь в виду, что в остальной части настоящей книги для иллюстрации различных концепций будет применяться как утилита ildasm.exe, так и утилита reflector.exe. Поэтому загрузите утилиту Reflector, если это еще не было сделано. Развертывание исполняющей среды .NET Нетрудно догадаться, что сборки .NET могут выполняться только на той машине, на которой установлена платформа .NET Framework. Для разработчиков программного обеспечения .NET это не должно оказываться проблемой, поскольку их машина надлежащим образом конфигурируется еще во время установки распространяемого бесплатно пакета NET Framework 4.0 SDK (а также таких коммерческих сред для разработки .NET-приложений, как Visual Studio 2010). В случае развертывания сборки на компьютере, на котором платформа .NET не была установлена, сборка запускаться не будет. Для таких ситуаций Microsoft предлагает специальный установочный пакет dotNetFx4 0_Full_x8 6 .exe, который может бесплатно
Глава 1. Философия .NET 77 поставляться и устанавливаться вместе со специальным программным обеспечением. Этот пакет доступен для загрузки на сайте Microsoft в общем разделе загружаемых продуктов (http: //www.microsoft. com/downloads). После установки пакета dotNetFx40_Full_x86.exe на целевой машине появятся необходимые библиотеки базовых классов .NET, исполняющая среда .NET (mscoree.dll) и дополнительная инфраструктура .NET (такая как GAC). На заметку! Операционные системы Windows Vista и Windows 7 изначально сконфигурированы с необходимой инфраструктурой исполняющей среды .NET. В случае развертывания приложения в среде какой-то другой операционной системы производства Microsoft, например, Windows XP, нужно будет позаботиться об установке и настройке на целевой машине среды .NET. Клиентский профиль исполняющей среды .NET Установочная программа dotNetFx4 0_Full_x8 6.exe имеет объем примерно 77 Мбайт. Если для конечного пользователя обеспечивается возможность развертывать приложение с компакт-диска, это не будет представлять проблемы, поскольку установочная программа сможет просто запускать исполняемый файл тогда, когда машина не сконфигурирована надлежащим образом. Если пользователь должен будет загружать dotNetFx4 0_Full_x8 6 .exe по медленному соединению с Интернетом, ситуация несколько усложняется. Для разрешения подобной проблемы в Microsoft разработали альтернативную установочную программу — так называемый клиентский профиль (dotNetFx40_Client_x8 6.exe), который тоже доступен для бесплатной загрузки на сайте Microsoft. Эта установочная программа, как не трудно догадаться по ее названию, предусматривает выполнение установки подмножества библиотек базовых классов .NET в дополнение к необходимой инфраструктуре исполняющей среды. Поскольку она имеет гораздо меньший размер (примерно 34 Мбайт), установку тех же самых библиотек, что появляются при полной установке .NET, на целевой машине она не обеспечивает. При желании не охватываемые ею сборки могут быть добавлены на целевую машину при выполнении пользователем обновления Windows (с помощью службы Windows Update). На заметку! Как у полного, так и у клиентского профиля исполняющей среды имеются 64-разрядные аналоги, которые называются, соответственно, dotNetFx40_Full_x86_x64.exe и dotNetFx40 Client x86 x64.exe. Не зависящая от платформы природа .NET В завершение настоящей главы хотелось бы сказать несколько слов о не зависящей от платформы природе .NET. К удивлению большинства разработчиков, сборки .NET могут разрабатываться и выполняться в средах операционных систем производства не Microsoft, в частности — в Mac OS X, различных дистрибутивах Linux, Solaris, а также на устройствах типа iPhone производства Apple (через API-интерфейс MonoTbuch). Чтобы понять, что делает подобное возможным, необходимо рассмотреть еще одну используемую в мире .NET аббревиатуру — CLI, которая расшифровывается как Common Language Infrastructure (Общеязыковая инфраструктура). Вместе с языком программирования С# и платформой .NET в Microsoft был также разработан набор официальных документов с описанием синтаксиса и семантики языков С# и CIL, формата сборок .NET, ключевых пространств имен и технических деталей работы гипотетического механизма исполняющей среды .NET (названного виртуальной системой выполнения — Virtual Execution System (VES)).
78 Часть I. Общие сведения о языке С# и платформе .NET Все эти документы были поданы в организацию Ecma International (http: //www. ecma-international.org) и утверждены в качестве официальных международных стандартов. Среди них наибольший интерес представляют: • документ ЕСМА-334, в котором содержится спецификация языка С#; • документ ЕСМА-335, в котором содержится спецификация общеязыковой инфраструктуры (CLI). Важность этих документов становится очевидной с пониманием того факта, что они предоставляют третьим сторонам возможность создавать дистрибутивы платформы .NET для любого количества операционных систем и/или процессоров. Среди этих двух спецификаций документ ЕСМА-335 является более "объемным", причем настолько, что был разбит на шесть разделов, которые перечислены в табл. 1.4. Таблица 1.4. Разделы спецификации CLI Разделы документа п ЕСМА-335 Предназначение Раздел I. Концепции В этом разделе описана общая архитектура CLI, в том числе правила и архитектура CTS и CLS и технические детали функционирования механизма среды выполнения .NET Раздел II. Определение В этом разделе описаны детали метаданных и формат сборок в .NET метаданных и семантика Раздел III. Набор В этом разделе описан синтаксис и семантика кода CIL инструкций CIL Раздел IV. Профили В этом разделе дается общий обзор тех минимальных и полных биб- и библиотеки лиотек классов, которые должны поддерживаться в дистрибутиве .NET Раздел V. В этом разделе описан формат обмена деталями отладки Раздел VI. Дополнения В этом разделе представлена коллекция дополнительных и более конкретных деталей, таких как указания по проектированию библиотек классов и детали по реализации компилятора CIL Следует иметь в виду, что в разделе IV (Профили и библиотеки) описан лишь минимальный набор пространств имен, в которых содержатся ожидаемые от дистрибутива CLI службы (наподобие коллекций, консольного ввода-вывода, файлового ввода-вывода, многопоточной обработки, рефлексии, сетевого доступа, ключевых средств защиты и возможностей для манипулирования XML-данными). Пространства имен, которые упрощают разработку веб-приложений (ASP.NET), доступ к базам данных (ADO.NET) и создание настольных приложений с графическим пользовательским интерфейсом (Windows Forms /Windows Presentation Foundation) в CLI не описаны. Хорошая новость состоит в том, что в главных дистрибутивах .NET библиотеки CLI дополняются совместимыми с Microsoft эквивалентами ASP.NET, ADO.NET и Windows Forms, чтобы предоставлять полнофункциональные платформы для разработки приложений производственного уровня. На сегодняшний день популярностью пользуются две основных реализации CLI (помимо самого предлагаемого Microsoft и рассчитанного на Windows решения). Хотя настоящая книга и посвящена главным образом созданию .NET-приложений с помощью поставляемого Microsoft дистрибутива .NET, в табл. 1.5 приведена краткая информация касательно проектов Mono и Portable.NET.
Глава 1. Философия .NET 79 Таблица 1.5. Дистрибутивы .NET, распространяемые с открытым исходным кодом Дистрибутив Описание http: / /www. mono-pro j ect. com Проект Mono представляет собой распространяемый с открытым исходным кодом дистрибутив CLI, который ориентирован на различные версии Linux (например, openSuSE, Fedora и т.п.), а также Windows и устройства Mac OS X и iPhone http: //wwwmdotgnu. org Проект Portable.NET представляет собой еще один распространяемый с открытым исходным кодом дистрибутив CLI, который может работать в целом ряде операционных систем. Он нацелен охватывать как можно больше операционных систем (Windows, AIX, BeOS, Mac OS X, Solaris и все главные дистрибутивы Linux) Как в Mono, так и в Portable.NET предоставляется ЕСМА-совместимый компилятор С#, механизм исполняющей среды .NET, примеры программного кода, документация, а также многочисленные инструменты для разработки приложений, которые по своим функциональным возможностям эквивалентны поставляемым в составе .NET Framework 4.0 SDK. Более того, Mono и Portable.NET поставляются с компиляторами VB.NET, Java и С. На заметку! Описание приемов создания межплатформенных .NET-приложений с помощью Mono можно найти в приложении Б. Резюме Целью этой главы было предоставить базовые теоретические сведения, необходимые для изучения остального материала настоящей книги. Сначала были рассмотрены ограничения и сложности, которые существовали в технологиях, предшествовавших появлению .NET, а потом показано, как .NET и С# упрощают существующее положение вещей. Главную роль в .NET, по сути, играет механизм выполнения (mscoree . dll) и библиотека базовых классов (mscorlib.dll вместе с сопутствующими файлами). Общеязыковая среда выполнения (CLR) способна обслуживать любой двоичный файл .NET (сборку), который отвечает правилам управляемого программного кода. Как было показано в этой главе, в каждой сборке (помимо метаданных типов и манифеста) содержатся CIL- инструкции, которые с помощью JIT-компилятора преобразуются в инструкции, ориентированные на конкретную платформу. Помимо этого, здесь рассматривалась роль общеязыковой спецификации (CLS) и общей системы типов (CTS). После этого было рассказано о таких полезных утилитах для просмотра объектов, как ildasm.exe и reflector.exe, а также о том, как сконфигурировать машину для обслуживания приложений .NET с помощью полного и клиентского профилей. И, наконец, напоследок было вкратце упомянуто о преимуществах не зависящей от платформы природы С# и .NET, о чем более подробно пойдет речь в приложении Б.
ГЛАВА 2 Создание приложений на языке С# Программисту, использующему язык С#, для разработки .NET-приложений на выбор доступно много инструментов. Целью этой главы является совершение краткого обзорного тура по различным доступным средствам для разработки .NET- приложений, в том числе, конечно же, Visual Studio 2010. Глава начинается с рассказа о том, как работать с компилятором командной строки С# (esc. exe), и самым простейшим из всех текстовых редакторов Notepad (Блокнот), который входит в состав операционной системы Microsoft Windows, а также приложением Notepad++, доступным для бесплатной загрузки. Хотя для изучения приведенного в настоящей книге материала вполне хватило бы компилятора csc.exe и простейшего текстового редактора, читателя наверняка заинтересует использование многофункциональных интегрированных сред разработки (Integrated Development Environment — IDE). В этой главе также описана бесплатная IDE-среда с открытым исходным кодом SharpDevelop, предназначенная для разработки приложений .NET. Как будет показано, по своим функциональным возможностям эта IDE-среда не уступает многим коммерческим аналогам. Кроме того, в главе кратко рассматривается IDE-среда Visual C# 2010 Express (распространяемая бесплатно), а также ключевая функциональность Visual Studio 2010. На заметку! В ходе этой главы будут встречаться синтаксические конструкции С#, которые пока еще не рассматривались. Официальное изучение языка С# начнется в главе 3 Роль комплекта .NET Framework 4.0 SDK Одним из мифов в области разработки .NET-приложений является то, что программистам якобы обязательно требуется приобретать копию Visual Studio для того, чтобы разрабатывать программы на С#. На самом деле, создавать .NET-программу любого рода можно с помощью распространяемого бесплатно и доступного для загрузки комплекта инструментов для разработки программного обеспечения .NET Framework 4.0 SDK (Software Development Kit). В этом пакете поставляются многочисленные управляемые компиляторы, утилиты командной строки, примеры кода, библиотеки классов .NET и полная справочная система. На заметку! Программа установки .NET Framework 4.0 SDK (dotnetf x4 Of ullsetup. exe) доступна на странице загрузки .NET по адресу http: //msdn.microsoft.com/netframework.
Глава 2. Создание приложений на языке С# 81 Тем, кто планирует использовать Visual Studio 2010 или Visual C# 2010 Express, следует иметь в виду, что в установке .NET Framework 4.0 SDK нет никакой необходимости. При установке любого из упомянутых продуктов этот пакет SDK устанавливается автоматически и сразу же предоставляет все необходимое. Если использование IDE-среды от Microsoft для проработки материала настоящей книги не планируется, обязательно установите .NET Framework 4.0 SDK, прежде чем двигаться дальше. Окно командной строки в Visual Studio 2010 При установке .NET Framework 4.0 SDK, Visual Studio 2010 или Visual C# 2010 Express на локальном жестком диске создается набор новых каталогов, в каждом из которых содержатся разнообразные инструменты для разработки .NET-приложений. Многие из этих инструментов работают в режиме командной строки, и чтобы использовать их в любом каталоге, нужно сначала соответствующим образом зарегистрировать пути к ним в операционной системе. Для этого можно обновить переменную среды PATH вручную, но лучше пользоваться предлагаемым в Visual Studio окном командной строки (Command Prompt). Чтобы открыть это окно (рис. 2.1), необходимо выбрать в меню Start (Пуск) пункт All Programs^ Microsoft Visual Studio 2010^Visual Studio Tools (Все программы^ Microsoft Visual Studio 20Ю1^Инструменты Visual Studio). [Setting environment for using Microsoft Visual Studio 2010 x86 tools. C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC> Рис. 2.1. Окно командной строки в Visual Studio 2010 Преимущество применения именно этого окна командной строки связано с тем, что оно уже сконфигурировано на предоставление доступа к каждому из инструментов для разработки .NET-приложений. При условии, что на компьютере развернута среда разработки .NET, можно попробовать ввести следующую команду и нажать клавишу <Enter>: esc -? Если все в порядке, появится список аргументов командной строки, которые может принимать работающий в режиме командной строки компилятор С# (esc означает C-sharp compiler]. Создание приложений на С# с использованием esc. ехе В действительности необходимость в создании крупных приложений с использованием одного лишь компилятора командной строки С# может никогда не возникнуть, тем не менее, важно понимать в общем, как вручную компилировать файлы кода. Существует несколько причин, по которым освоение этого процесса может оказаться полезным.
Часть I. Общие сведения о языке С# и платформе .NET Самой очевидной причиной является отсутствие Visual Studio 2010 или какой-то другой графической IDE-среды. Работа может выполняться в университете, где использование инструментов для генерации кода и IDE-сред обычно запрещено. Планируется применение автоматизированных средств разработки, таких как msbuild. ехе, которые требуют знать опции командной строки для используемых инструментов. Возникло желание углубить свои познания в С#. В графических IDE-средах в конечном итоге все заканчивается предоставлением компилятору esc. ехе инструкций относительно того, что следует делать с входными файлами кода С#. В этом отношении изучение происходящего "за кулисами'' позволяет получить необходимые знания. Еще одно преимущество подхода с использованием одного лишь компилятора esc. ехе состоит в том, что он позволяет обрести навыки и чувствовать себя более уверенно при работе с другими инструментами командной строки, входящими в состав .NET Framework 4.0 SDK. Как будет показано далее, целый ряд важных утилит работает исключительно в режиме командной строки. Чтобы посмотреть, как создавать .NET-приложение без IDE-среды, давайте построим с помощью компилятора С# и текстового редактора Notepad простую исполняемую сборку по имени TestApp.exe. Сначала необходимо подготовить исходный код. Откройте программу Notepad (Блокнот), выбрав в меню Start (Пуск) пункт All Programs1^ Accessories1^ Notepad (Все программы ^Стандартные ^Блокнот), и введите следующее типичное определение класса на С#: // Простое приложение на языке С#. using System; class TestApp { static void Main() { Console.WriteLine ("Testing! 1, 2, 3"); } } После окончания ввода сохраните файл (например, в каталоге С: \CscExample) под именем TestApp. cs. Теперь давайте ознакомимся с ключевыми опциями компилятора С#. На заметку! По соглашению всем файлам с кодом на С# назначается расширение * .cs. Имя файла не нуждается в специальном отображении на имя какого-либо типа или типов. Указание целевых входных и выходных параметров Первым делом важно разобраться с тем, как указывать имя и тип создаваемой сборки (т.е., например, консольное приложение по имени MyShell.exe, библиотека кода по имени MathLib.dll или приложение Windows Presentation Foundation по имени Halo8.exe). Каждый из возможных вариантов имеет соответствующий флаг, который нужно передать компилятору esc. ехе в виде параметра командной строки (табл. 2.1). На заметку! Параметры, передаваемые компилятору командной строки (а также большинству других утилит командной строки), могут сопровождаться префиксом в виде символа дефиса (-) или символа косой черты (/).
Глава 2. Создание приложений на языке С# 83 Таблица 2.1. Выходные параметры, которые может принимать компилятор С# Параметр Описание /out /target:exe /target:library /targetrmodule /target:winexe Этот параметр применяется для указания имени создаваемой сборки По умолчанию сборке присваивается то же имя, что у входного файла *. cs Этот параметр позволяет создавать исполняемое консольное приложение. Сборка такого типа генерируется по умолчанию, потому при создании подобного приложения данный параметр можно опускать Этот параметр позволяет создавать однофайловую сборку * . dll Этот параметр позволяет создавать модуль. Модули являются элементами многофайловых сборок (и будут более подробно рассматриваться в главе 14) Хотя приложения с графическим пользовательским интерфейсом можно создавать с применением параметра /target: exe, параметр /target: winexe позволяет предотвратить открытие окна консоли под остальными окнами Чтобы скомпилировать TestApp. cs в консольное приложение TextApp.exe, перейдите в каталог, в котором был сохранен файл исходного кода: cd C:\CscExample Введите следующую команду (обратите внимание, что флаги должны обязательно идти перед именем входных файлов, а не после): esc /target:exe TestApp.cs Здесь флаг /out не был указан явным образом, поэтому исполняемый файл получит имя TestApp. exe из-за того, что именем входного файла является TestApp. Кроме того, для почти всех принимаемых компилятором С# флагов поддерживаются сокращенные версии написания, наподобие /t вместо /target (полный список которых можно увидеть, введя в командной строке команду esc -?). esc /t:exe TestApp.cs Более того, поскольку флаг /t: exe используется компилятором как выходной параметр по умолчанию, скомпилировать TestApp. cs также можно с помощью следующей простой команды: esc TestApp.cs Теперь можно попробовать запустить приложение TestApp . exe из командной строки, введя имя его исполняемого файла, как показано на рис. 2.2. J Administrator Visual Studio Command Prompt '■*»«*' *'*" »■ ■-, f- t: \CscExamp1 e>TestApp.exe [Testing! 1, 2, 3 ]C:\CscExamp1e> i Рис. 2.2. Приложение TestApp в действии
84 Часть I. Общие сведения о языке С# и платформе .NET Добавление ссылок на внешние сборки Давайте посмотрим, как скомпилировать приложение, в котором используются типы, определенные в отдельной сборке .NET. Если осталось неясным, каким образом компилятору С# удалось понять ссылку на тип System. Console, вспомните из главы 1, что во время процесса компиляции происходит автоматическое добавление ссылки на mscorlib.dll (если по какой-то необычной причине нужно отключить эту функцию, следует передать компилятору csc.exe параметр /nostdlib). Модифицируем приложение TestApp так, чтобы в нем открывалось окно сообщения Windows Forms. Для этого откройте файл TestApp. cs и измените его следующим образом: using System; // Добавить эту строку: using System.Windows.Forms; class TestApp { static void Main() { Console.WriteLine("Testing! 1, 2, 3"); // И добавить эту строку: MessageBox.Show("Hello..."); Рис. 2.3. Первое приложение Windows Forms } Обратите внимание на импорт пространства имен System. Windows .Forms с помощью поддерживаемого в С# ключевого слова using (о котором рассказывалось в главе 1). Вспомните, что явное перечисление пространств имен, которые используются внутри файла * .cs, позволяет избегать необходимости указывать полностью уточненные имена типов. Далее в командной строке нужно проинформировать компилятор esc. ехе о том, в какой сборке содержатся используемые пространства имен. Поскольку применялся класс MessageBox из пространства имен System. Windows . Forms, значит, нужно указать компилятору на сборку System.Windows .Forms .dll, что делается с помощью флага /reference (или его сокращенной версии /г): esc /r:System.Windows.Forms.dll TestApp.cs Если теперь снова попробовать запустить приложение, то помимо консольного вывода в нем должно появиться еще и окно с сообщением, как показано на рис. 2.3. Добавление ссылок на несколько внешних сборок Кстати, как поступить, когда необходимо указать esc. ехе несколько внешних сборок? Для этого нужно просто перечислить все сборки через точку с запятой. В рассматриваемом примере ссылаться на несколько сборок не требуется, но ниже приведена команда, которая иллюстрирует перечисление множества сборок: esc /r: System. Windows . Forms ,dll;System. Drawmg.dll *.cs На заметку! Как будет показано позже в настоящей главе, компилятор С# автоматически добавляет ссылки на ряд ключевых сборок .NET (таких как System. Windows . Forms . dll), даже если они не указаны с помощью флага /г.
Глава 2. Создание приложений на языке С# 85 Компиляция нескольких файлов исходного кода В текущем примере приложение TestApp. exe создавалось с использованием единственного файла исходного кода * . cs. Хотя определять все типы .NET в одном файле * . cs вполне допустимо, в большинстве случаев проекты формируются из нескольких файлов *. cs для придания кодовой базе большей гибкости. Чтобы стало понятнее, давайте создадим новый класс и сохраним его в отдельном файле по имени HelloMsg. cs. // Класс HelloMessage using System; using System.Windows.Forms; class HelloMessage { public void Speak() { MessageBox.Show("Hello..."); } } Изменим исходный класс TestApp так, чтобы в нем использовался класс этого нового типа, и закомментируем прежнюю логику Windows Forms: using System; // Эта строка больше не нужна: // using System.Windows.Forms; class TestApp { static void Mam () { Console .WriteLme ("Testing ' 1, 2, 3"); // Эта строка тоже больше не нужна: // MessageBox.Show("Hello..."); // Используем класс HelloMessage: HelloMessage h = new HelloMessage(); h. Speak () ; } } Чтобы скомпилировать файлы исходного кода на С# , необходимо их явно перечислить как входные файлы: esc /r:System.Windows.Forms.dll TestApp.cs HelloMsg.cs В качестве альтернативного варианта компилятор С# позволяет использовать групповой символ (*) для включения в текущую сборку всех файлов * .cs, которые содержатся в каталоге проекта: esc /г:System.Windows.Forms.dll *.cs Вывод, получаемый после запуска этой программы, идентичен предыдущей программе. Единственное отличие между этими двумя приложениями связано с разнесением логики по нескольким файлам. Работа с ответными файлами в С# Как не трудно догадаться, для создания сложного приложения С# из командной строки потребовалось бы вводить утомительное количество входных параметров для уведомления компилятора о том, как он должен обрабатывать исходный код. Для облег-
86 Часть I. Общие сведения о языке С# и платформе .NET чения этой задачи в компиляторе С# поддерживается использование так называемых ответных файлов (response files). В ответных файлах С# размещаются все инструкции, которые должны использоваться в процессе компиляции текущей сборки. По соглашению эти файлы имеют расширение * . rsp (сокращение от response— ответ). Чтобы посмотреть на них в действии, давайте создадим ответный файл по имени TestApp. rsp, содержа ищи следующие аргументы (комментарии в данном случае обозначаются символом #): # Это ответный файл для примера # TestApp.exe из главы 2. # Ссылки на внешние сборки: /г:System.Windows.Forms.dll # Параметры вывода и подлежащие компиляции файлы # (здесь используется групповой символ): /target:exe /out:TestApp.exe *.cs Теперь при условии сохранения данного файла в том же каталоге, где находятся подлежащие компиляции файлы исходного кода на С#, все приложение можно будет создать следующим образом (обратите внимание на применение символа @): esc @TestApp.rsp В случае необходимости допускается также указывать и несколько ответных * . rsp файлов в качестве входных параметров (например, esc @FirstFile.rsp @SecondFile . rsp @ThirdFile . rsp). При таком подходе, однако, следует иметь в виду, что компилятор обрабатывает параметры команд по мере их поступления. Следовательно, аргументы командной строки, содержащиеся в поступающем позже файле * . rsp, могут переопределять параметры из предыдущего ответного файла. Еще важно обратить внимание на то, что все флаги, перечисляемые явным образом перед ответным файлом, будут переопределяться настройками, которые содержатся в этом файле. То есть в случае ввода следующей команды: esc /outiMyCoolApp.exe @TestApp.rsp имя сборки будет по-прежнему выглядеть как TestApp. exe (а не MyCoolApp. exe) из-за того, что в ответном файле TestApp. rsp содержится флаг /out: TestApp. exe. В случае перечисления флагов после ответного файла они будут переопределять настройки, содержащиеся в этом файле. На заметку! Действие флага /reference является кумулятивным. Где бы не указывались внешние сборки (перед, после или внутри ответного файла), в конечном итоге каждая из них все равно будет добавляться к остальным. Используемый по умолчанию ответный файл (esc. rsp) Последним моментом, связанным с ответными файлами, о котором необходимо упомянуть, является то, что с компилятором С# ассоциирован ответный файл esc. rsp, который используется по умолчанию и размещен в том же самом каталоге, что и файл esc .exe (обычно это С: \Windows\Microsof t .NET\Framework\<BepcMH>, где на месте элемента <версия> идет номер конкретной версии платформы). Открыв файл esc. rsp в программе Notepad (Блокнот), можно увидеть, что в нем с помощью флага /г: указано множество сборок .NET, в том числе различные библиотеки для разработки веб-приложений, программирования с использованием технологии LINQ и обеспечения доступа к данным и прочие ключевые библиотеки (помимо, конечно же, самой главной библиотеки mscorlib.dll).
Глава 2. Создание приложений на языке С# 87 При создании программ на С# с применением с~.с . ехе ссылка на этот ответный файл добавляется автоматически, даже когда указан специальный файл * . rsp. Из-за наличия такого ответного файла по умолчанию, рассматриваемое приложение TestApp. ехе можно скомпилировать и помощью следующей команды (поскольку в esc . rsp уже содержится ссылка на System. Windows . Forms . dll): esc /out: TestApp .ехе *.cr. Для отключения функции автоматического чтения файла esc. rsp укажите опцию /noconfig: esc @TestApp.rsp /noconfig На заметку! В случае добавления (с помощью опции /г) ссылок на сборки, которые на самом деле не используются, компилятор их проигнорирует Поэтому беспокоиться по поводу "разбухания кода" не нужно. Понятно, что у компилятора командной строки С# имеется множество других параметров, которые можно применять для управления генерацией результирующей сборки .NET. Другие важные возможности будут демонстрироваться по мере необходимости далее в книге, а полные сведения об этих параметрах можно всегда найти в документации .NET Framework 4.0 SDK Исходный код. Код приложения CscExample доступен в подкаталоге Chapter 2. Создание приложений .NET с использованием Notepad++ Еще одним текстовым редактором, о котором следует кратко упомянуть, является распространяемое с открытым исходным кодом бесплатное приложение Notepad++. Загрузить его можно по адресу http: //notepad-plus . sourceforge .net/. В отличие от простого редактора Notepad (Блокнот), поставляемого в составе Windows, приложение Notepad++ позволяет создавать код на множестве различных языков и поддерживает установку разнообразных дополнительных подключаемых модулей. Помимо этого, Notepad++ обладает рядом других замечательных достоинств, в том числе: • изначальной поддержкой для использования ключевых слов С# (и их кодирования цветом включительно); • поддержкой для свертывания синтаксиса (syntax folding), позволяющей сворачивать и разворачивать группы операторов в коде внутри редактора (и подобной той, что предлагается в Visual Studio 2010/C# 2010 Express); • возможностью увеличивать и уменьшать масштаб отображения текста с помощью колесика мыши (имитирующего действие клавиши <Ctrl>); • настраиваемой функцией автоматического завершения (autocompletion) различных ключевых слов С# и названий пространств имен .NET. Чтобы активизировать поддержку функции автоматического завершения кода на С# (рис. 2.4), необходимо одновременно нажать клавиши <Ctrl> и пробела. На заметку! Список вариантов, предлагаемых для автоматического завершения кода в отображаемом окне, можно изменять и расширять. Для этого необходимо открыть файл С: \Program Files\Notepad++\plugms\APIs\cs . xml для редактирования и добавить в него любые дополнительные записи.
88 Часть I. Общие сведения о языке С# и платформе .NET t:\CscE.4antpleNHellcMjg.« - Ncte^d - ♦ ' File Edit Search View Format Language Settings Macro Run TextFX Plugins Window ? o..|H^oe&l4'tufcl*ci|#lk|4t.|- s 1. 1 .. ♦ Ы HetoMag cs | i!li|X>< • // Tbe HelloMessage class using System; using System.Windows.Forms; using clas; 3< й с System.Web.Configuration System.Web.Hosting System.Web.Mail I System.Web.Services C* 148 chars 172 bytes 12 lines Ln:4 Col: 7 Sel: 0 @ bytes) in 0 ranges Dos\Windows ANSI Рис. 2.4. Использование функции автоматического завершения кода в Notepad++ Более подробно о приложении Notepad++ здесь рассказываться не будет. Чтобы узнать о нем больше, воспользуйтесь предлагаемым в его меню ? пунктом Help (Справка). Создание приложений.NET с помощью SharpDevelop Нельзя не согласиться с тем, что написание кода С# в приложении Notepad++, несомненно, является шагом в правильном направлении по сравнению с использованием редактора Notepad (Блокнот) и командной строки. Тем не менее, в Notepad++ отсутствуют богатые возможности IntelliSense, визуальные конструкторы для построения графических пользовательских интерфейсов, шаблоны проектов, инструменты для работы с базами данных и многое другое. Для удовлетворения перечисленных потребностей больше подходит такой рассматриваемый далее продукт для разработки .NET-приложений, как SharpDevelop (также называемый #Develop). Продукт SharpDevelop представляет собой распространяемую с открытым исходным кодом многофункциональную IDE-среду, которую можно применять для создания .NET- сборок с помощью С# ,VB, CIL, а также Python-образного .NET-языка под названием Boo. Помимо того, что эта IDE-среда предлагается совершенно бесплатно, интересно обратить внимание на тот факт, что она сама реализована полностью на С#. Для установки среды SharpDevelop необходимо загрузить и скомпилировать ее файлы * . cs вручную или запустить готовую программу setup. ехе. Оба дистрибутива доступны по адресу http://www.sharpdevelop.com/. IDE-среда SharpDevelop обладает массой достоинств в плане улучшения продуктивности. Наиболее важными из них являются: • поддержка для множества языков и типов проектов .NET; • функция IntelliSense, завершение кода и возможность использования только определенных фрагментов кода; • диалоговое окно Add Reference (Добавление ссылки), позволяющее легко добавлять ссылки на внешние сборки, в том числе и те, что находятся в глобальном кэше сборок (Global Assembly Cache — GAC); • визуальный конструктор Windows Forms; • встроенные утилиты для просмотра объектов и определения кода; • визуальные утилиты для проектирования баз данных; • утилита для преобразования кода на С# в код на VB (и наоборот).
Глава 2. Создание приложений на языке С# 89 Впечатляюще для бесплатной IDE-среды, не так ли? Ниже кратко рассматриваются некоторые наиболее интересные достоинства из перечисленных выше. На заметку! На момент написания книги в текущей версии SharpDevelop пока не поддерживались средства С# 2010 / .NET 4.0. Периодически заглядывайте на веб-сайт SharpDevelop, чтобы проверить, не появились ли следующие выпуски среды. Создание простого тестового проекта После установки SharpDevelop за счет выбора в меню File (Файл) пункта New^Solution (Создать1^ Решение) можно указывать, какой тип проекта требуется сгенерировать (и на каком языке .NET). Например, предположим, что нужно создать проект по имени MySDWinApp типа Windows Application (Приложение Windows) на языке С# (рис. 2.5). Рис. 2.5. Диалоговое окно создания нового проекта в SharpDevelop Как и в Visual Studio, в SharpDevelop предлагается окно элементов управления для конструктора графических пользовательских интерфейсов Windows Forms (позволяющее перетаскивать элементы управления на поверхность конструктора) и окно Properties (Свойства), позволяющее настраивать внешний вид и поведение каждого из элементов графического пользовательско