Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
VPS в 21 локации

От 104 рублей в месяц

Безлимитный трафик. Защита от ДДоС.

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

Книги: [Классика] [Базы данных] [Internet/WWW] [Сети] [Программирование] [UNIX] [Windows] [Безопасность] [Графика] [Software Engineering] [ERP-системы] [Hardware]

     

C# и платформа .NET. Библиотека программиста

Троелсен Э.

Издано: Издательский дом "Питер"
ISBN: 5-318-00750-3
Мягкий переплет, 800 стр.

Начало
Cодержание
Отрывок
[Заказать книгу в магазине "Мистраль"]

Отрывок

Философия .NET

Любому современному программисту, который желает идти в ногу с последними веяниями, каждые несколько лет приходится переучиваться. Языки (C++, Visual Basic, Java), библиотеки (MFC, ATL, STL), архитектуры (COM, CORBA), которые стали вехами в развитии программирования за последние годы, постепенно уходят в тень лучших или по крайней мере более молодых программных технологий. Вне зависимости от того, нравится это программистам или нет, этот процесс неизбежен. Платформа .NET компании Microsoft - это следующая волна коренных изменений, которая идет к нам из Редмонда.

В этой главе мы рассмотрим основополагающие понятия .NET, к которым мы затем будем обращаться во всех остальных частях книги. Глава начинается с рассмотрения элементов, на которых основана платформа .NET - сборок (assemblies), промежуточного языка (intermediate language, IL) и компиляции в процессе выполнения (just in time compilation, JIT). Мы также рассмотрим взаимосвязи компонентов платформы .NET - Common Language Runtime (CLR), Common Type System (CTS) и Common Language Specification (CLS).

В этой главе также приведен общий обзор возможностей, которые предоставляют библиотеки базовых классов .NET и утилит (таких как ILDasm.exe), которые помогут вам в работе с этими библиотеками. В самом конце главы мы познакомимся с возможностями компиляции приложений C# с использованием компилятора командной строки (csc.exe) и интегрированной среды разработки (Integrated Development Environment, IDE) Visual Studio.NET.

Современное состояние дел

Перед тем как мы вступим в пределы вселенной .NET, полезно будет разобраться с простым вопросом - а зачем, собственно говоря, она нужна? Для этого мы очень кратко рассмотрим те технологии, которые имеются в распоряжении программистов в настоящий момент, их возможности и ограничения, а затем перейдем к тем преимуществам, которые предоставляют C# и платформа .NET в целом.

Как живут программисты, использующие Win32/C

Изначально под программированием под Windows подразумевалось программирование на C с использованием Windows Application Programming Interface (интерфейсом прикладного программирования Windows, в 32-разрядных версиях Windows - Win32 API). С использованием этой технологии было создано множество вполне достойных приложений, однако вряд ли кто-нибудь будет спорить с тем, что написание приложения с использованием только Windows API - это очень трудоемкая задача.

Еще одна проблема заключается в том, что C - достаточно суровый по отношению к программисту язык. Тем, кто создает на нем свои приложения, приходится вручную заниматься управлением памятью, выполнять расчеты при использовании указателей и работать с совершенно неестественными с точки зрения человеческого языка синтаксическими конструкциями. Кроме того, в C, конечно, недостаточно возможностей для объектно-ориентированного программирования. Если вам приходилось связывать тысячи глобальных функций Win32 API в единые гигантские конструкции, вы не будете сильно удивляться тому, что в подобных приложениях ошибки встречаются очень часто.

Как живут программисты, использующие C++/MFC

C++ - это огромный шаг вперед в отношении новых возможностей по сравнению с исходным языком С. Во многих ситуациях C++ вполне допустимо представить как объектно-ориентированную надстройку над C. Такая надстройка позволяет использовать преимущества "столпов" объектно-ориентированного программирования - инкапсуляции, полиморфизма и наследования. Однако программисты, использующие C++, остаются незащищенными от многих и часто опасных особенностей C (теми же самыми низкоуровневыми возможностями работы с памятью и трудными для восприятия синтаксическими конструкциями).

Существует множество библиотек для C++, основное назначение которых - облегчить написание приложений под Windows, предоставив для этой цели уже готовые классы. Одна из наиболее распространенных библиотек - это MFC (Microsoft Foundation Classes). MFC - это дополнительный уровень над Win32 API, который значительно упрощает работу программиста за счет использования готовых классов, макросов и мастеров. Однако MFC - это лишь частичное решение проблемы. Даже при использовании MFC программисту приходится работать со сложным для чтения кодом, весьма опасным с точки зрения возможных ошибок.

Как живут программисты, использующие Visual Basic

Люди всегда стремятся сделать свою жизнь проще. Повинуясь этому стремлению многие программисты на C++ обратили свои взоры к гораздо более простому и дружелюбному языку, каким является Visual Basic (VB). Visual Basic позволяет работать с достаточно сложными элементами интерфейса пользователя, библиотеками кода (например, COM-серверами) и средствами доступа к данным при минимальных затратах времени и сил. Visual Basic в гораздо большей степени, чем MFC, прячет от пользователя вызовы Win32 API и предоставляет большой набор интегрированных средств быстрой разработки.

Однако у Visual Basic есть и недостатки. Главный из них - это гораздо меньшие возможности, которые предоставляет этот язык, по сравнению с С++ (это утверждение справедливо, по крайней мере, для версий более ранних, чем VB.NET). Visual Basic - это язык "для работы с объектами", а не объектно-ориентированный язык в обычном понимании этого слова. В Visual Basic нет классического наследования, нет поддержки создания параметризованных классов, нет собственных средств создания многопоточных приложений - и этот список можно продолжать еще долго.

Как живут программисты, использующие Java

Язык программирования Java - это полностью объектно-ориентированный язык, который в отношении синтаксиса многое унаследовал от C++. Конечно, преимущества Java далеко не исчерпываются межплатформенностью. Язык Java в синтаксическом отношении проще и логичнее, чем C++. Java как платформа предоставляет в распоряжение программистов большое количество библиотек (пакетов), в которых содержится большое количество описаний классов и интерфейсов на все случаи жизни. С их помощью можно создавать стопроцентные приложения Java с возможностью обращения к базам данных, поддержкой передачи почтовых сообщений, с клиентской частью, которой необходим только web-браузер, или наоборот, с клиентской частью, обладающей изощренным интерфейсом.

Java - это очень элегантный и красивый язык. Однако при его использовании проблем также избежать не удастся. Одна из серьезных проблем заключается в том, что при создании сложного приложения на Java вам придется использовать только этот язык для создания всех частей этого приложения. В Java предусмотрено не так уж много средств для межъязыкового взаимодействия (что понятно ввиду предназначения Java быть единым многоцелевым языком программирования). В реальном мире существуют миллионы строк готового кода, которые хотелось бы интегрировать с новыми приложениями на Java. Однако это сделать очень трудно.

Java - это далеко не идеальный язык во многих ситуациях. Простой пример - если вы попытаетесь создать только на Java приложение, активно работающее с 3D-графикой, скорее всего, вы обнаружите, что работать такое приложение будет не очень быстро. Немного подумав, вы можете прийти к выводу, что для работы с 3D-графикой лучше использовать код, написанный на языке с более развитыми низкоуровневыми возможностями (например, на C++). Однако интегрировать такой код с кодом на Java вам будет очень сложно. Поскольку возможности для обращения к API компонентов, созданных на других языках, в Java очень ограничены, говорить о реальном межъязыковом взаимодействии на основе Java не приходится.

Как живут COM-программисты

Современное состояние дел таково, что если вы не строите Java-приложения, то велика вероятность, что вы осваиваете технологию Microsoft Component Object Model (COM). COM-технология провозглашает: "Если вы создаете классы в точном соответствии с требованиями COM, то у вас получится блок повторно используемого программного кода".

Прелесть двоичного COM-сервера заключается в том, что к нему можно обращаться из любого языка. Например, программисты, использующие C++, могут создавать классы, которые можно будет использовать из приложения на VBasic. Программисты, использующие Delphi, могут использовать классы, созданные на C и т. д. Однако в межъязыковом взаимодействии COM есть свои ограничения. Например, вы не можете произвести новый тип COM от существующего (то есть не можете использовать классическое наследование). Для повторного использования существующих типов COM вам придется использовать другие, гораздо менее надежные и эффективные средства.

Большое преимущество COM заключается в том, что программист может не заботиться о физическом местонахождении компонентов. Такие средства, как Application Identifiers (AppIDs, идентификаторы приложений), стабы (stubs), прокси, среда выполнения COM, позволяют избегать при обращении к компонентам по сети необходимости помещать в приложение код для работы с сокетами, вызовами RPC и прочими низкоуровневыми механизмами. Достаточно посмотреть на такой код на Visual Basic 6.0 для клиента COM:

'Этот код на VB 6.0 предназначен для активации класса COM,
'созданного на любом языке. Класс COM может быть расположен
'в любом месте на локальном компьютере или в сети.
Dim c as New MyCOMClass 'Местонахождение класса 'определяется через AppID c.DoSomeWork

Объектная модель COM используется очень широко. Однако внутреннее устройство компонентов весьма сложно. Чтобы научиться разбираться в нем, вам придется потратить по крайней мере несколько месяцев. Написание приложений с использованием COM-компонентов можно упростить, используя стандартные библиотеки, например библитеку Active Template Library (ATL) со своим набором готовых классов, шаблонов и макросов.

Некоторые языки (например, Visual Basic) также позволяют скрыть сложность инфраструктуры COM. Однако всех сложностей избежать все равно не удастся. Например, даже если вы работаете с относительно простым и поддерживающим COM Visual Basic, вам придется решать не всегда простые вопросы, связанные с регистрацией компонентов на компьютере и развертыванием приложений.

Как живут программисты, использующие Windows DNA

Картина будет неполной, если мы не примем во внимание такую мелочь, как Интернет. За несколько последних лет Microsoft добавила в свои операционные системы большое количество средств для работы с этой средой, в том числе и средства, призванные помочь в создании Интернет-приложений. Однако построение законченного web-приложения с использованием технологии Windows DNA (Distributed iNternet Architecture - распределенная межсетевая архитектура) до сих пор остается весьма сложной задачей.

Значительная часть сложностей возникает оттого, что Windows DNA требует использования разнородных технологий и языков (ASP, HTML, XML, JavaScript, VBScript, COM(+), ADO и т. д.). Одна из проблем заключается в том, что синтаксически все эти языки и технологии очень мало похожи друг на друга. Например, синтаксис JavaScript больше похож на синтаксис C, в то время как VBScript является подмножеством Visual Basic. COM-серверы, предназначенные для работы в среде выполнения COM+, созданы на основе совершенно иных подходов, нежели ASP-страницы, которые к ним обращаются. Конечным результатом является пугающее смешение технологий. Помимо всего прочего, в каждом языке, который входит в состав Windows DNA, предусмотрена своя система типов, что также не является источником большой радости для программистов. Например, тип данных int в JavaScript - это не то же самое, что int в C, который, в свою очередь, отличен от integer в Visual Basic.

Решение .NET

На этом мы будем считать обращение к новейшей истории программирования законченным. Главный вывод, с которым вряд ли кто-нибудь будет спорить, таков: тяжела жизнь Windows-программиста. На этом фоне возможности, предлагаемые платформой .NET, позволяют радикально облегчить нашу жизнь. Один из главных принципов .NET звучит так: "Изменяйте все, что хотите, откуда вам угодно". .NET - это совершенно новая модель для создания приложений под Windows (а в будущем, видимо, и под другими операционными системами). Вот краткое перечисление основных возможностей .NET:

  • Полные возможности взаимодействия с существующим кодом. Вряд ли кто-нибудь будет спорить, что это - вещь очень хорошая. Как мы увидим в главе 12, существующие двоичные компоненты COM отлично работают вместе с двоичными файлами .NET.
  • Полное и абсолютное межъязыковое взаимодействие. В отличие от классического COM, в .NET поддерживаются межъязыковое наследование, межъязыковая обработка исключений и межъязыковая отладка.
  • Общая среда выполнения для любых приложений .NET, вне зависимости от того, на каких языках они были созданы. Один из важных моментов при этом - то, что для всех языков используется один и тот же набор встроенных типов данных.
  • Библиотека базовых классов, которая обеспечивает сокрытие всех сложностей, связанных с непосредственным использованием вызовов API, и предлагает целостную объектную модель для всех языков программирования, поддерживающих .NET.
  • Про пугающую сложность COM можно забыть! IClassFactory, IUnknown, код IDL и проклятые VARIANT-совместимые типы данных (BSTR, SAFEARRAY и остальные) больше не встретятся вам в коде программ .NET.
  • Действительное упрощение процесса развертывания приложения. В .NET нет необходимости регистрировать двойные типы в системном реестре. Более того, .NET позволяет разным версиям одного и того же модуля DLL мирно сосуществовать на одном компьютере.

    Строительные блоки .NET (CLR, CTS и CLS)

    Технологии CLR, CTS и CLS очень важны для понимания смысла платформы .NET. С точки зрения программиста .NET вполне можно рассматривать просто как новую среду выполнения и новую библиотеку базовых классов. Среда выполнения .NET как раз и обеспечивается с помощью Common Language Runtime (CLR, стандартная среда выполнения для языков). Главная роль CLR заключается в том, чтобы обнаруживать и загружать типы .NET и производить управление ими в соответствии с вашими командами. CLR берет на себя всю низкоуровневую работу - например, автоматическое управление памятью, межъязыковым взаимодействием, развертывание (с отслеживанием версий) различных двоичных библиотек.

    Еще один строительный блок платформы .NET - это Common Type System (CTS, стандартная система типов). CTS полностью описывает все типы данных, поддерживаемые средой выполнения, определяет, как одни типы данных могут взаимодействовать с другими и как они будут представлены в формате метаданных .NET (подробнее о метаданных будет рассказано ниже в этой главе).

    Важно понимать, что не во всех языках программирования .NET обязательно должны поддерживаться все типы данных, которые определены в CTS. Common Language Specification (CLS) - это набор правил, определяющих подмножество общих типов данных, в отношении которых гарантируется, что они безопасны при использовании во всех языках .NET. Если вы создаете типы .NET с использованием только тех возможностей, которые совместимы с CLS, тем самым вы сделаете их пригодными для любых языков .NET.

    Библиотека базовых классов .NET

    Помимо спецификаций CLR и CTS/CLS платформа .NET предоставляет в ваше распоряжение также библиотеку базовых классов, доступную из любого языка программирования .NET. Библиотека базовых классов не только прячет обычные низкоуровневые операции, такие как файловый ввод-вывод, обработка графики и взаимодействие с оборудованием компьютера, но и обеспечивает поддержку большого количества служб, используемых в современных приложениях.

    В качестве примера можно привести встроенные типы для обращения к базам данных, работы с XML, обеспечения безопасности при работе приложения, создания приложений для работы в Web (конечно, обеспечивается и поддержка обычных консольных и оконных приложений). С концептуальной точки зрения отношения между уровнем среды выполнения и библиотекой базовых классов .NET выглядят так, как показано на рис. 1.1.

    Рис. 1.1. Отношения между средой выполнения и
    библиотекой базовых классов .NET

    Преимущества C#

    Специально для платформы .NET Microsoft был разработан новый язык программирования C#. C# - это язык программирования, синтаксис которого очень похож на синтаксис Java (но не идентичен ему). Например, в C# (как в Java) определение класса состоит из одного файла (*.cs), в отличие от C++, где определение класса разбито на заголовок (*.h) и реализацию (*.cpp). Однако называть C# клоном Java было бы неверно. Как C#, так и Java основаны на синтаксических конструкциях C++. Если Java во многих отношениях можно назвать очищенной версией C++, то C# можно охарактеризовать как очищенную версию Java.

    Синтаксические конструкции C# унаследованы не только от C++, но и от Visual Basic. Например, в C#, как и в Visual Basic, используются свойства классов. Как C++, C# позволяет производить перегрузку операторов для созданных вами типов (Java не поддерживает ни ту, ни другую возможность). C# - это фактически гибрид разных языков. При этом C# синтаксически не менее (если не более) чист, чем Java, так же прост, как Visual Basic, и обладает практически той же мощью и гибкостью, что и C++. Подводя итоги, еще раз выделим основные особенности C#.

  • Указатели больше не нужны! В программах на C#, как правило, нет необходимости в работе с ними (однако если вам это потребуется, пожалуйста, - возможности для работы с указателями в вашем распоряжении).
  • Управление памятью производится автоматически.
  • В C# предусмотрены встроенные синтаксические конструкции для работы с перечислениями, структурами и свойствами классов.
  • В C# осталась возможность перегружать операторы, унаследованные от C++. При этом значительная часть возникавших при этом сложностей ликвидирована.
  • Предусмотрена полная поддержка использования программных интерфейсов. Однако в отличие от классического COM применение интерфейсов - это не единственный способ работы с типами, используя различные двоичные модули. .NET позволяет передавать объекты (как ссылки или как значения) через границы программных модулей.
  • Также предусмотрена полная поддержка аспектно-ориентированных программных технологий (таких как атрибуты). Это позволяет присваивать типам характеристики (что во многом напоминает COM IDL) для описания в будущем поведения данной сущности.

    Возможно, самое важное, что необходимо сказать про язык C#, - это то, что он генерирует код, предназначенный для выполнения только в среде выполнения .NET. Например, вы не сможете использовать C# для создания классического COM-сервера. Согласно терминологии Microsoft код, предназначенный для работы в среде выполнения .NET, - это управляемый код (managed code). Двоичный файл, который содержит управляемый файл, называется сборкой (assembly). Подробнее об этом будет сказано ниже.

    Языки программирования .NET

    Во время анонса платформы .NET на конференции 2000 Professional Developers Conference (PDC) докладчики называли фирмы, работающие над созданием .NET-версий своих компиляторов. На момент написания этой книги компиляторы для создания .NET-версий приложений разрабатывались более чем для 30 различных языков. Помимо четырех языков, поставляемых с Visual Studio.NET (C#, Visual Basic.NET, "Managed C++" и JScript.NET), ожидаются .NET-версии Smalltalk, COBOL, Pascal, Python, Perl и множества остальных известных языков программирования. Общая картина представлена на рис. 1.2.

    Рис. 1.2. Все компиляторы, ориентированные на .NET,
    генерируют IL-инструкции и метаданные

    Может показаться смешным, но двоичные файлы .NET, для которых используются стандартные расширения DLL и EXE, по своему внутреннему содержанию не имеют абсолютно ничего общего с обычными исполняемыми файлами. Например, файлы DLL не предоставляют свои методы в распоряжение приложений на компьютере. В отличие от компонентов COM двоичные файлы .NET не описываются с помощью кода IDL и регистрируются в системном реестре. Однако, пожалуй, самое важное отличие заключается в том, что двоичные файлы .NET не содержат зависящих от платформы команд. Содержимое двоичных файлов .NET - это платформенно-независимый "промежуточный язык", который официально называется Microsoft Intermediate Language (MSIL, промежуточный язык Microsoft), или просто IL.

    Обзор двоичных файлов .NET ("сборки")

    Когда с помощью компилятора для платформы .NET создается модуль DLL или EXE, содержимое этого модуля - это так называемая сборка (assembly) на языке IL. Мы будем рассматривать сборки более подробно в главе 6. Однако для того, чтобы лучше понять особенности среды выполнения .NET, нам потребуется охарактеризовать хотя бы некоторые базовые свойства этого нового формата исполняемых файлов.

    Как уже говорилось, сборка содержит код на "промежуточном языке" - IL. Назначение IL концептуально аналогично байт-коду Java - он компилируется в платформенно-специфичные инструкции, только когда это абсолютно необходимо. "Абсолютная необходимость" возникает тогда, когда к блоку инструкций IL (например, реализации метода) обращается для использования среда выполнения .NET.

    Помимо инструкций IL, двоичные модули .NET содержат также метаданные, которые подробно описывают все типы, использованные в модуле. Например, если у вас внутри сборки есть класс Foo, то в метаданных этой сборки будет содержаться информация о базовом классе для Foo, какие интерфейсы предусмотрены для Foo (если они вообще предусмотрены), а также полное описание всех методов, свойств и событий этого класса.

    Во многих отношениях метаданные .NET являются значительным усовершенствованием по сравнению с классической информацией о типах в COM. Классические двоичные файлы COM обычно описываются с помощью ассоциированной библиотеки типов (которая очень похожа на двоичную версию кода IDL). Проблема с информацией о типах в COM заключается в том, что никто не может гарантировать вам, что эта информация окажется полной. Код IDL не может создать полный каталог внешних серверов, которые могут быть необходимы для нормальной работы содержащихся в модуле COM классов. Существование метаданных .NET, напротив, обеспечивается тем, что метаданные автоматически генерируются компилятором, создающим приложение .NET.

    Метаданные описывают не только типы, используемые в сборке, но и саму сборку. Эта часть метаданных называется манифестом (manifest). В манифесте содержится информация о текущей версии сборки, об использованных ограничениях по безопасности, о поддерживаемом естественном языке (английском, русском и т. д.), а также список всех внешних сборок, которые потребуются для нормального выполнения. Нам предстоит рассмотреть в этой главе различные средства, которые можно использовать для анализа кода IL внутри сборки, метаданных для типов и манифеста.

    Сборки из одного и нескольких файлов

    В подавляющем большинстве случаев двоичный файл .NET и сборка - это одно и то же и между ними существует отношение "один-к-одному". Если мы будем говорить о создании .NET DLL, то понятия "двоичный файл" и "сборка" мы будем использовать как синонимы. Однако (подробнее об этом - в главе 6) такой подход верен не всегда. Сборка может состоять как из одного, так и из нескольких двоичных файлов. В сборке из одного файла (single file assembly) этот единственный файл содержит и манифест, и метаданные, и инструкции IL.

    В сборке из нескольких двоичных файлов (multifile assembly) каждый двоичный файл называется модулем (module). При создании таких многофайловых сборок один из двоичных файлов должен содержать манифест сборки (в нем могут также находиться и другие данные, в том числе инструкции IL). Все остальные модули могут содержать только метаданные типов и инструкции IL.

    Зачем может потребоваться создание многофайловой сборки? Единственная причина для этого - большая гибкость при развертывании приложения. Например, если пользователь обращается к удаленной сборке, которая должна быть загружена на его локальный компьютер, среда выполнения загрузит лишь те модули сборки, которые действительно необходимы. Такое решение позволит избежать ненужного сетевого трафика и увеличить скорость работы программы.

    Роль Microsoft Intermediate Language

    Теперь, когда вы уже имеете представление о сборках .NET, мы можем рассмотреть Microsoft Intermediate Language (IL) - промежуточный язык Microsoft, более подробно. Код IL не зависит от платформы, на которой будет производиться выполнение. При этом компилятор для платформы .NET сгенерирует код IL вне зависимости от того, какой язык программирования (C#, Visual Basic.NET, Eiffel и т. п.) вы использовали для создания программы. Пожалуй, есть смысл продемонстрировать это более наглядно. В качестве примера мы создадим не самый сложный калькулятор. Единственное, что он у нас будет уметь делать - производить сложение 10 и 84. Ниже приведен код этого калькулятора на С#. Пока можно не задумываться об особенностях синтаксиса в этом примере, но отметьте для себя код, относящийся к методу Add().

    // С пространствами имен мы познакомимся чуть ниже в этой главе 
    namespace Calculator
    {
    	using System;
    		
    	// В классе Calculator определен метод Add(), а также точка входа 
    	// приложения - метод Main() 
    	public class Calc
    	{
    		// Конструктор по умолчанию
    		public Calc(){}
    
    		public int Add(int x, int y)
    		{
    			return x + y;
    		}
    
    		public static int Main(string[] args)
    		{
    			// Создаем объект Calc и складываем два числа
    			Calc c = new Calc();
    			int ans = c.Add(10, 84);
    			Console.WriteLine("10 + 84 is {0}.", ans);
    			return 0;
    		}
    	}
    }
    

    После того как этот исходный файл будет обработан компилятором C# (csc.exe), в нашем распоряжении окажется исполняемый файл C# - сборка из одного файла. Внутри этого файла можно будет обнаружить манифест, инструкции IL и метаданные, описывающие класс Calc. Если мы заглянем внутрь этой сборки (с помощью чего - об этом мы скажем ближе к концу этой главы), то помимо всего прочего мы сможем найти следующий блок инструкций IL, относящихся к методу Add():
    .method public hidebysig instance int32 Add(int32 x, int32 y) il managed

    {
    	// Размер кода 8 (0x8)
    	.maxstack 2
    	.locals ([0] int32 V_0)
    	IL_0000: ldarg.1
    	IL_0001: ldarg.2
    	IL_0002: add
    	IL_0003: stloc.0
    	IL_0004: br.s IL_0006
    	IL_0006: ldloc.0
    	IL_0007: ret
    } // Конец кода IL для метода Calc:Add()
    

    Если большая часть строк в коде IL осталась для вас загадкой, не волнуйтесь. Код IL будет рассмотрен более подробно в главе 7. Пока самое важное - отметить, что компилятор C# генерирует не платформенно-зависимый набор инструкций, а код IL. То же самое справедливо и для других компиляторов .NET. Давайте создадим наш калькулятор на языке Visual Basic.NET:

    ' Калькулятор VB.NET
    Module Module1
    	' Опять-таки, в классе Calc определен метод Add() 
    	' и точка входа для приложения
    	Class Calc
    		
    		Public Function Add(ByVal x As Integer, ByVal y As Integer) As Integer
    			' Да! Теперь Visual Basic поддерживает ключевое слово 'return'
    			Return x + y
    		End function
    	End Class
    	Sub Main()
    		Dim ans As Integer
    		Dim c As New Calc()
    		ans = c.Add(10, 84)
    		Console.WriteLine("10 + 84 is {0}.", ans)
    	End Sub
    End Module
    Если мы поищем внутри сборки код, относящийся к методу Add(), то мы сможем обнаружить следующее:
    .method public instance int32 Add(int32 x, int32 y) il managed
    {
    	// Размер кода 11 (0xb)
    	.maxstack 2
    	.locals init ([0] int32 Add)
    	IL_0000: nop
    	IL_0001: ldarg.1
    	IL_0002: ldarg.2
    	IL_0003: add.ovf
    	IL_0004: stloc.0
    	IL_0005: nop
    	IL_0006: br.s IL_0008
    	IL_0008: nop
    	IL_0009: ldloc.0
    	IL_000a: ret
    } // Конец метода Module1$Calc::Add
    

    Как мы видим, получившийся код IL практически идентичен. Незначительные отличия возникают вследствие особенностей компиляторов C# и Visual Basic.NET.

    Код приложений CSharpCalculator и VBCalculator можно найти в подкаталоге Chapter 1.

    Преимущества IL

    Возможно, к этому времени у вас уже созрел вопрос - а в чем, собственно, вообще могут состоять преимущества IL перед обычным набором платформенно-зависимых инструкций? Одно из преимуществ, про которое мы уже говорили, - возможность полного межъязыкового взаимодействия. Поскольку любой код на любом языке программирования .NET компилируется в стандартный набор инструкций IL, проблем во взаимодействии между блоками кода IL не будет. При этом взаимодействие будет производиться, как и положено, на двоичном уровне.

    Еще одно возможное преимущество - потенциальная независимость от компьютерной платформы. Существует большая вероятность, что среда выполнения .NET будет распространена на самые разные компьютерные платформы и операционные системы (отличные от Windows). В результате .NET может пойти по стопам Java - то есть с помощью языков .NET можно будет создавать программы, которые будут работать под самыми разными операционными системами (и при этом в отличие от Java еще и пользоваться преимуществами языковой независимости!) Таким образом, .NET потенциально позволяет создавать приложения на любом языке, которые будут работать на любой платформе и под любой операционной системой.

    Однако в отношении межплатформенности пока ключевое слово - "потенциально". На момент создания этой книги Microsoft официально не произнесла ни слова относительно возможности портирования среды выполнения .NET под другие операционные системы. Поэтому пока мы будем считать, что приложения .NET работают только под Windows.

    Роль метаданных

    Программистам, работающим с COM, хорошо знакома концепция Interface Definition Language (IDL, языка определения интерфейсов). IDL - это "метаязык", который позволяет, исключив любую двусмысленность, описать типы, используемые внутри сервера COM. IDL компилируется в двоичный формат (называемый библиотекой типов) с использованием компилятора midl.exe. Этот компилятор может использоваться любым языком, предназначенным для работы с COM.

    IDL полностью описывает все типы данных, используемые в двоичном файле COM, но информация о самом этом двоичном файле в нем минимальна. Фактически она ограничивается номером версии (к примеру, 1.0, 2.0 или 2.4) и информацией о локализации (например, English, German, Russian). Кроме того, наличие или отсутствие метаданных (и их полноту) должен вручную контролировать создающий сервер COM программист - таким образом, необходимых метаданных в двоичном файле COM может вообще не оказаться.

    В отличие от COM при использовании платформы .NET вам вообще не придется думать об IDL. Однако общий принцип описания типов в строго определенном двоичном формате остался.

    Сборки .NET всегда содержат полные и точные метаданные, поскольку метаданные в них генерируются автоматически. Как и в IDL, в метаданных .NET содержится исчерпывающая информация об абсолютно всех типах, которые используются в сборке (классах, структурах, перечислениях и прочем), а также о каждом свойстве, методе или событии каждого типа.

    Еще одно отличие метаданных .NET от информации IDL заключается в том, что метаданные .NET гораздо более подробны. В них перечислены все ссылки на внешние модули и сборки, которые потребуются для нормального выполнения сборки .NET. За счет этого можно считать сборки .NET фактически самодокументируемыми. В результате, к примеру, отпадает необходимость регистрировать двоичные файлы .NET в системном реестре (подробнее об этом будет сказано ниже).

    Простой пример метаданных

    Вот пример метаданных для метода Add() нашего приложения CSharpCalculator (метаданные для этого метода в VBCalculator будут точно такими же):

    Method #2
    ___________________________
    MethodName	: Add (06000002)
    Flags		: [Public] [HideBySig] [ReuseSlot] (00000086)
    RVA		: 0x00002058
    ImplFlags	: [IL] [Managed] (00000000)
    CalcCnvntn	: [DEFAULT]
    hasThis
    ReturnType	: I4
    2 Arguments
    	Argument #1: I4
    	Argument #2: I4
    2 Parameters
    	(1) ParamToken : (08000001) Name : x flags: [none] (00000000) default:
    	(2) ParamToken : (08000002) Name : y flags: [none] (00000000) default:
    

    В коде ясно представлены название метода, тип возвращаемого значения и данные об ожидаемых аргументах. Как мы помним, вручную никаких метаданных мы не писали - за нас все сделал компилятор C#.

    Кто будет обращаться к метаданным? И сама среда выполнения .NET (очень часто), и самые разные средства разработки и отладки. Например, средство IntelliSense в Visual Studio.Net (которое пытается помочь вам закончить начатую строку) берет необходимую ему информацию именно из метаданных. Метаданные активно используются утилитами просмотра, отладки и, конечно, самим компилятором C#.

    Компиляция IL в платформенно-зависимые инструкции

    Поскольку в сборках, как мы выяснили, содержится платформенно-независимый код IL, а выполняются в конечном итоге именно платформенно-зависимые инструкции, кто-то должен взять на себя работу по компиляции IL в такие инструкции. Этот "кто-то" называется "just-in-time compiler" (JIT) - компилятор времени выполнения. JIT часто ласково называют "jitter" (дрожание, трепет). JIT для перевода IL в платформенно-зависимые инструкции входит в состав среды выполнения .NET. Используя код IL, разработчики могут не думать об особенностях архитектуры CPU данного компьютера - эти особенности будут учтены JIT.

    Откомпилированные из IL платформенно-зависимые инструкции JIT помещает в кэш-памяти, что очень сильно ускоряет работу приложения. Предположим, был вызван метод Bar() класса Foo. При первом вызове этого метода JIT откомпилирует относящийся к этому методу код IL в платформенно-зависимые инструкции. При повторных вызовах этого метода JIT уже не будет заниматься компиляцией, а просто возьмет уже готовый откомпилированный код из кэша в оперативной памяти.

    Типы .NET и пространства имен .NET

    Сборка (не важно, однофайловая или многофайловая) может содержать любое количество самых разных типов. В мире .NET тип - это общий термин, который может относиться к классам, структурам, интерфейсам, перечислениям и прочему. При создании приложения .NET (например, на языке C#) вам потребуется организовывать взаимодействие этих типов. Например, сборка может определять класс с несколькими интерфейсами; один интерфейс может принимать в качестве параметров только значения определенного перечисления.

    У вас есть возможность использовать пространства имен при создании ваших собственных типов. Пространство имен - это логическая структура для организации имен, используемых в приложении .NET. Основное назначение пространств имен - предупредить возможные конфликты между именами в разных сборках.

    Вот пример: вы создаете приложение типа Windows Forms, которое обращается к двум внешним сборкам. В каждой сборке есть тип с именем GoCart, при этом эти типы отличаются друг от друга. При написании кода вы можете точно указать, к какому именно типу и из какой сборки вы обращаетесь. Для этого достаточно к имени типа добавить имя соответствующего пространства имен: например, CustomVehicals.GoCart или SlowVehicals.GoCart. Более подробно мы разберем применение пространств имен ниже в этой главе.

    Основы Common Language Runtime - среды выполнения .NET

    После того как мы познакомились с типами, сборками, метаданными и IL, настало время рассмотреть среду выполнения .NET - CLR более подробно. Среду выполнения (runtime) можно рассматривать как набор служб, необходимых для работы блока программного кода. К таким службам можно отнести и требуемые библиотеки. Например, если вы создали приложение MFC, то в качестве компонента среды выполнения вам потребуется весьма объемистая библиотека времени выполнения Microsoft Foundation Classes - mfc42.dll. Программы на Visual Basic привязаны к такому компоненту среды выполнения, как библиотека msvbvm60.dll, а программам на Java необходим большой набор файлов, входящих в состав виртуальной машины Java.

    Своя среда выполнения требуется и приложениям .NET. Главное отличие этой среды выполнения от всех тех, которые были перечислены выше, заключается в том, что единая среда выполнения .NET используется приложениями, написанными на любых языках программирования .NET. Как уже говорилось выше, среда выполнения .NET носит официальное название Common Language Runtime (CLR).

    Сама CLR состоит из двух главных компонентов. Первый компонент - это ядро среды выполнения, которое реализовано в виде библиотеки mscoree.dll. При обращении к приложению .NET mscoree.dll автоматически загружается в память, и, в свою очередь, эта библиотека управляет процессом загрузки в память сборки данного приложения. Ядро среды выполнения ответственно за множество задач. Оно занимается поиском физического местонахождения сборки, обнаружением внутри сборки запрошенного типа (класса, интерфейса, структуры и т. п.) на основе информации метаданных, компилирует IL в платформенно-зависимые инструкции, выполняет проверки, связанные с обеспечением безопасности, - и этот перечень далеко не полон.

    Второй главный компонент CLR - это библиотека базовых классов. Сама библиотека разбита на множество отдельных сборок, однако главная сборка библиотеки базовых классов представлена файлом mscorlib.dll. В библиотеке базовых классов содержится огромное количество типов для решения распространенных задач при создании приложения. Приложение .NET будет обязательно использовать сборку mscorlib.dll и по мере необходимости - другие сборки (как встроенные, так и создаваемые вами самими).

    На рис. 1.3 представлен путь, который проходит исходный код приложения, прежде чем воплотиться в выполнение каких-либо действий на компьютере.

    Стандартная система типов CTS

    Мы уже говорили, что стандартная система типов (Common Type System, CTS) - это формальная спецификация, которая определяет, как какой-либо тип (класс, структура, интерфейс, встроенный тип данных и т. п.) должен быть определен для его правильного восприятия средой выполнения .NET. CTS определяет синтаксические конструкции (в качестве примера можно взять перегрузку операторов), которые могут поддерживаться, а могут и не поддерживаться конкретным языком программирования .NET. Если вы хотите создавать сборки, которые смогут использоваться всеми языками программирования .NET, вам придется при создании типов следовать правилам Common Language Specification - CLS. А сейчас мы рассмотрим особенности тех типов, к которым применяется спецификация CTS.

    Классы CTS

    Концепция классов - краеугольный камень любого объектно-ориентированного программирования. Она поддерживается всеми языками программирования .NET. Класс (class) - это набор свойств, методов и событий, объединенных в единое целое. Как, наверное, вы и предполагали, в CTS предусмотрены абстрактные члены классов, что обеспечивает возможность применения полиморфизма в производных классах. Множественное наследование в CTS запрещено. Самые важные характеристики классов представлены в табл. 1.1.

    Рис. 1.3. Роль среды выполнения .NET

    Таблица 1.1. Самые важные характеристики классов CTS

    Характеристика Ее смысл
    Является ли класс "закрытым"? Закрытые классы не могут становиться базовыми для других классов
    Предусмотрены ли в классе какие-либо интерфейсы? Интерфейс - это набор абстрактных членов, который обеспечивает связь между объектом и пользователем. В CTS в классе может быть любое количество интерфейсов
    Является ли класс абстрактным? Объекты абстрактных классов создать невозможно. Единственное назначение абстрактных классов - выполнять роль базовых для других классов. За счет механизма наследования абстрактные классы обеспечивают производные классы общими наборами членов
    Какова область видимости для данного класса? Для каждого класса должен быть определен атрибут области видимости (visibility). Как правило, значение этого атрибута определяет, можно ли обращаться к этому классу из внешних сборок или только из той, которая его содержит

    Структуры CTS

    Помимо классов в CTS также предусмотрена концепция структур (structures). Если вы работали с C, возможно, вы удивитесь, что этот пользовательский тип данных сохранился и в мире .NET (правда, надо отметить, что он немного изменился). В принципе, структуры можно грубо рассматривать как упрощенные разновидности классов (подробнее о различиях между классами и структурами будет рассказано в главе 2). Структуры CTS могут иметь любое количество конструкторов с параметрами (конструктор без параметров зарезервирован). С помощью конструкторов с параметрами вы можете установить значение любого поля объекта структуры в момент создания этого объекта. Например:

    // Определяем структуру C#
    struct Baby
    {
    	// В структуре могут быть поля:
    	public string name;
    	// В структуре можно определить конструкторы (но только c параметрами): 
    	public Baby (string name)
    	{this.name = name; }
    		
    	// В структурах могут быть определены методы:
    	public void Cry()
    	{ Console.WriteLine ("Waaaaaaaaaaah!!!"); }
    
    	public bool IsSpleeping() { return false; }
    	public bool IsChanged() { return false; }
    }
    

    А вот наша структура в действии:

    // Добро пожаловать в мир малютки Макса!
    
    Baby barnaBaby = new Baby ("Max");
    Console.WriteLine ("Changed?: {0}", barnaBaby.IsChanged().ToString());
    
    Console.WriteLine ("Sleeping?: {0}", barnaBaby.IsSleeping().ToString());
    

    // А теперь Макс покажет нам свою подлинную сущность:
    for(int i=0; i<10000; i++)
    barnaBaby.Cry();

    Все CTS-совместимые структуры произведены от единого базового класса System.ValueType. Этот базовый класс определяет структуру как тип данных для работы только со значениями, но не с ссылками. В структуре может быть любое количество интерфейсов. Однако структуры не могут быть унаследованы от остальных типов данных и они всегда являются "закрытыми" - то есть они не могут выступать в качестве базовых для целей наследования.

    Интерфейсы CTS

    Интерфейсы (interfaces) - это просто наборы абстрактных методов, свойств и определений событий. В отличие от классической технологии COM, интерфейсы .NET не являются производными от единого общего интерфейса, каким в мире COM был интерфейс IUnknown. В интерфейсах самих по себе смысла не очень много. Однако если мы знаем, что какой-либо класс реализует известный нам интерфейс, мы вправе требовать от этого класса определенной функциональности. При создании своего собственного интерфейса на .NET-совместимом языке программирования вы можете произвести этот интерфейс сразу от нескольких базовых интерфейсов. Подробнее про использование интерфейсов в программах на C# будет рассказано в главе 6.

    Члены типов CTS

    Как мы уже говорили, в классах и структурах может быть любое количество членов. Член (member) - это либо метод, либо свойство, либо поле, либо событие. Подробнее про члены типов в мире .NET будет рассказано в нескольких ближайших главах. Однако нам сейчас важно отметить, что для любого члена в .NET существует набор характеристик.

    Например, любой член в .NET характеризуется своей областью видимости (public, private, protected и т. д.). Член можно объявить как абстрактный, чтобы воспользоваться возможностями полиморфизма при работе с производными классами. Члены могут быть статическими (static, такие члены могут совместно использоваться всеми объектами данного класса) и обычными - принадлежащими только одному объекту данного класса.

    Перечисления CTS

    Перечисление (enumeration) - это удобная программная конструкция, которая позволяет вам объединять пары имя - значение под указанным вами именем перечисления. Предположим, что вы создаете компьютерную игрушку, в которой играющий сможет выбирать из трех типов персонажей - волшебника (Wizard), воина (Fighter) или вора (Thief). В этой ситуации очень удобно будет воспользоваться перечислением:

    // Перечисление C#:
    enum PlayerType
    { Wizard=100, Fighter=200, Thief=300 };

    В CTS все перечисления являются производными от единственного базового класса System.Enum. Как мы убедимся в будущем, этот базовый класс содержит множество полезных членов, которые помогут нам в извлечении (и выполнении прочих операций) с парами имя - значение.

    Делегаты CTS

    Делегаты (delegates) - в мире .NET это безопасный для типов эквивалент указателя на функцию в C. Однако между ними есть и существенное отличие. Делегат .NET - это уже не просто адрес в оперативной памяти, а класс, производный от базового класса MulticastDelegate. Делегаты очень полезны в тех ситуациях, когда вам нужно, чтобы одна сущность передала вызов другой сущности. Делегаты - это краеугольный камень в технологии обработки событий .NET (об этом - в главе 5).

    Встроенные типы данных CTS

    Конечно же, в .NET предусмотрен богатый набор встроенных типов данных. Помимо всего прочего, этот набор еще и един для всех языков программирования .NET. Названия типов данных в языках .NET могут выглядеть по-разному, но эти названия - всего лишь псевдонимы для встроенных системных типов данных .NET, определенных в библиотеке базовых типов. Перечень встроенных типов данных .NET представлен в табл. 1.2.

    Таблица 1.2. Встроенные типы данных CTS

    Встроенный тип данных .NET Название в Visual Basic.NET Название в C# Название в Managed C++
    System.Byte Byte byte char
    System.SByte Не поддерживается sbyte signed char
    System.Int16 Short short short
    System.Int32 Integer int int или long
    System.Int64 Long long _int64
    System.UInt16 Не поддерживается ushort unsigned short
    System.UInt32 Не поддерживается uint unsigned int или unsigned long
    System.UInt64 Не поддерживается ulong unsigned _int64
    System.Single Single float float
    System.Double Double double double
    System.Object Object object Object*
    System.Char Char char _wchar_t
    System.String String string String*
    System.Decimal Decimal decimal Decimal
    System.Boolean Boolean bool bool

    Как видно из таблицы, не все языки .NET могут работать с некоторыми встроенными типами данных CTS. Поэтому очень важно определить такой набор типов (и программных конструкций), с которым гарантированно смогут работать любые .NET-совместимые языки программирования. Такой набор есть, и он называется CLS.

    Основы CLS

    Нет необходимости доказывать, что одни и те же программные конструкции в разных языках выглядят абсолютно по-разному. Например, в C# объединение строк (конкатенация) производится с помощью оператора плюс (+), в то время как в Visual Basic для этой же цели используется амперсанд (&). А вот как выглядит в разных языках функция, не возвращающая значений:

    ' Функция (подпроцедура) VBasic, которая ничего не
    ' возвращает (возвращает значение типа void):

    Public Sub Foo()
    	' Что-то делаем...
    End Sub
    
    // Такая же функция в C#:
    public void Foo()
    {
    	// Делаем то же самое...
    }
    

    Как мы уже видели, для среды выполнения .NET такая разница в синтаксисе абсолютно безразлична: все равно соответствующие компиляторы (в нашем случае vbc.exe и csc.exe) создадут одинаковый код IL. Однако языки программирования отличаются не только синтаксисом, но и возможностями. Например, в одних языках программирования разрешена перегрузка операторов, а в других - нет. Одни языки могут использовать беззнаковые (unsigned) типы данных, а в других такие типы данных не предусмотрены. Вывод очевиден - нам нужны некие единые правила для всех языков .NET. Если мы им следуем, то гарантируется, что программные модули, написанные на разных языках, будут нормально взаимодействовать друг с другом. Такой набор правил определен в спецификации CLS (Common Language Specification).

    Набор правил, определяемый CLS, не только гарантирует нормальное взаимодействие блоков кода, созданных на разных языках. Такой набор правил еще и определяет минимальные требования, которые предъявляются к любому .NET-совместимому компилятору. Необходимо помнить, что в CLS - это лишь часть тех возможностей, которые определены в CTS.

    Правилам CLS должны удовлетворять и инструментальные средства среды разработки - если мы хотим обеспечить межъязыковое взаимодействие, они должны генерировать только такой код, который соответствует требованиям CLS. У каждого правила CLS есть простое название (например, CLS Rule 6 - правило CLS номер 6). Вот пример одного из правил (это самое важное правило - правило номер 1):

  • Правило 1. Правила CLS относятся только к тем частям типа, которые предназначены для взаимодействия за пределами сборки, в которой они определены.

    Из этого правила явствует, что во внутренней логике при реализации какого-либо типа вы можете сколько угодно нарушать правила CLS - это ни на что не повлияет. Например, предположим, что вы создаете приложение .NET, которое взаимодействует с внешним миром с помощью трех классов, а в каждом из этих классов есть только одна функция. Правилам CLS в этом случае должны удовлетворять только три этих функции-члена (в отношении области видимости, соглашений об именовании, типов принимаемых параметров и т. д.). Во внутренней реализации этих функций, классов или приложения в целом может быть сколько угодно отступлений от правил CLS - за пределами вашего программного модуля никто об этом никогда не узнает.

    Конечно, в CLS существует не только правило 1, но и множество других правил. В CLS, к примеру, строгие требования предъявлены к представлению символьных значений, к определению перечислений, к использованию статических членов и т. д. Однако заучивать эти правила совершенно не обязательно (конечно, если вы не заняты созданием .NET-совместимого компилятора для своего собственного языка программирования). Если вам потребовалась дополнительная информация по CLS, произведите поиск в MSDN по словосочетанию "Collected CLS Rules".

    Работа с пространствами имен

    Мы закончили обзор среды выполнения .NET и теперь обратимся к особенностям библиотеки базовых классов .NET. Важность библиотек кода очевидна. Например, библиотека MFC определяет набор классов C++ для создания диалоговых окон, меню и панелей управления. В результате программисты могут не заниматься изобретением того, что давным-давно уже сделано до них, а сосредоточиться на уникальных аспектах создаваемого ими приложения. Аналогичные средства существуют и в Visual Basic, и в Java, и во всех остальных языках программирования.

    Однако в отличие от MFC, Visual Basic и Java в C# не существует библиотеки базовых классов только для этого языка. Можно сказать, что библиотека базовых классов C# вообще не существует. Вместо этого разработчики на C# используют библиотеку базовых типов для всей среды .NET. А для лучшей организации типов внутри этой библиотеки используется концепция пространств имен.

    Главное отличие от библиотек, привязанных к конкретному языку (типа MFC), заключается в том, что в любом .NET-совместимом языке используются те же самые типы и те же самые пространства имен, что и в C#. Вот три приложения (весьма напоминающих классическое Hello, World) на трех разных .NET-совместимых языках: C#, VB.NET и Managed C++ (MC++).

    // Привет от C#
    using System;
    public class MyApp
    {
    	public static void Main()
    	{
    		Console.WriteLine ("Hi from C#");
    	}
    }
    
    ' Привет от VB.NET
    Imports System
    Public Module MyApp
    
    	Sub Main()
    		Console.WriteLine ("Hi from VB");
    	End Sub
    End Module
    
    // Привет от Managed C++
    using namespace System;
    // Обратите внимание! Среда выполнения .NET в C++ сама собой помещает глобальную 
    // функцию main внутрь определения класса
    void main()
    {
    	Console::WriteLine("Hi from MC++");
    }
    

    Обратите внимание, что если нужен класс Console, то в любом языке .NET используется одно и то же пространство имен System. Если не обращать внимания на синтаксические различия, то код приложений на разных языках .NET практически идентичен. Платформе .NET свойственно изящество единого стиля программирования.

    Важнейшие пространства имен .NET

    Эффективность работы программиста, использующего .NET, напрямую зависит от того, насколько он знаком с тем массивом типов, который определен в пространствах имен библиотеки базовых классов. Самое важное пространство имен в C# - это System. В нем определены классы, которые обеспечивают самые важные функции C#. Вам не удастся создать ни одно работоспособное приложение C# без использования этого пространства имен.

    Пространство имен - это просто способ организации типов (классов, перечислений, интерфейсов, делегатов и структур) в единую группу. Конечно, обычно в одном пространстве имен объединяются взаимосвязанные типы. Например, тип System.Drawing содержит набор типов, которые призваны помочь вам в организации вывода изображений на графическое устройство. В .NET предусмотрены пространства имен для организации типов для работы с базами данными, Web, многопоточностью, защитой данных и множества других задач. В табл. 1.3 приведены некоторые (далеко не все) пространства имен .NET.

    Таблица 1.3. Пример пространства имен .NET

    Пространство имен .NET Назначение
    System Внутри - множество низкоуровневых классов для работы с простыми типами, выполнения математических операций, сборки мусора и т. п.
    System.Collections Для работы с контейнерными объектами, такими как ArrayList, Queue, SortedList
    System.Data Для обращений к базам данных. В книге этой теме посвящена
    System.Data.Common, System.Data.OleDb, System.Data.SqlClient специальная глава
    System.Diagnostics В этом пространстве имен содержатся многочисленные типы, используемые .NET-совместимыми языками для трассировки и отладки программного кода
    System.Drawing Типы для примитивов GDI+ - растровых изображений,
    System.Drawing.Drawing2D шрифтов, значков, поддержи печати. Предусмотрены также
    System.Drawing.Printing специальные классы для вывода более сложных изображений
    System.IO Как следует из названия, в этом пространстве имен объединены типы, отвечающие за операции ввода-вывода - в файл, буфер и т. п.
    System.Net Это пространство имен (как и все остальные, связанные с ним) содержит типы, относящиеся к передаче данных по сети (запрос - ответ, создание сокетов и т. п.)
    System.Reflection Классы, предназначенные для обнаружения, создания и вызова
    System.Reflection.Emit во время выполнения пользовательских типов
    System.Runtime.InteropServices Средства для взаимодействия с "традиционным" кодом
    System.Runtime.Remoting (Win32 DLL, COM-серверы) и типы, используемые для удаленного доступа (например, по коммутируемым соединениям)
    System.Security В мире .NET средства обеспечения безопасности интегрированы как со средой выполнения, так и с библиотекой базовых типов. В этом пространстве имен находятся классы для работы с разрешениями, криптографией и т. п.
    System.Threading Скорее всего, вы уже угадали - это пространство имен для типов, которые используются при работе с потоками (например, Mutex, Thread или Timeout)
    System.Web Классы, которые предназначены для использования в web-приложениях, включая ASP.NET
    System.Windows.Forms Классы для работы с элементами интерфейса Windows - окнами, элементами управления и прочим
    System.XML Множество классов для работы с данными в формате XML

    Использование пространств имен в коде приложения

    Как мы помним, пространство имен - это средство для логической группировки типов. С человеческой точки зрения выражение System.Console означает тип Console в пространстве имен System. Однако с точки зрения среды выполнения .NET System.Console - это единая сущность, к которой можно обратиться разными способами.

    Слово using нужно только вам - так проще будет обращаться к типам в конкретном пространстве имен. Если по каким-либо причинам использовать слово using вы не хотите, вполне можно обойтись и без него. Давайте рассмотрим это на примере. Предположим, что вы создаете обычное оконное приложение Windows, которое должно представлять на круговой диаграмме информацию, извлекаемую из базы данных. Кроме того, вам еще нужно поместить на главную форму своего приложения растровый рисунок с логотипом компании. Немного подумав, мы можем определить, что в нашем приложении нам потребуются классы из следующих пространств имен:

    //Пространства имен для использования в нашем приложении
    using System; //Без главного пространства имен не обойтись
    using System.Drawing; //для вывода изображений
    using System.Windows.Forms; //для элементов интерфейса
    using System.Data; //для доступа к базе данных using System.OleDb; //если к базе данных мы обращаемся по OLE DB

    После того как мы определим использование конкретного пространства имен (с использованием ключевого слова using), мы можем обращаться к типам, содержащимся в этом пространстве. Например, если нам потребовалось создать экземпляр класса Bitmap (определенном в пространстве имен System.Drawing), код может быть таким:

    //Явно указываем использование пространства имен:

    using System.Drawing;
    
    class MyClass
    {
    	public void DoIt()
    	{
    		//Создаем растровое изображение 20 на 20 пикселов
    		Bitmap bm = new Bitmap (20, 20);
    		...
    	}
    }
    

    Поскольку мы явно указали использование пространства имен System.Drawing с помощью ключевого слова using, компилятор сможет понять, что класс Bitmap - это член данного пространства имен. Если в примере, приведенном выше, мы опустим строку со словом using, мы получим сообщение компилятора об ошибке. Однако можно обойтись и без using, если использовать полное имя класса, как в следующем примере:

    //Обратите внимание - никаких указаний на пространства имен!

    class MyClass
    {
    	public void DoIt()
    	{
    		//Используем полное имя
    		System.Drawing.Bitmap bm = new.System.Drawing.Bitmap (20, 20);
    		...
    	}
    }
    

    Главная идея, я думаю, понятна: если вы явно указываете используемое пространство имен, строки при обращении к классам этого пространства имен получаются гораздо меньшего размера.

    Обращения к внешним сборкам

    Помимо того что вы можете явно указать используемое пространство имен с помощью ключевого слова using, иногда вам может потребоваться еще и явно указать физическое местонахождение сборки с необходимым кодом IL. Многие важнейшие пространства имен .NET физически связаны с файлом mscorlib.dll. Типы пространства имен System.Drawing физически "живут" внутри файла System.Drawing.dll. По умолчанию встроенные сборки .NET находятся в подкаталоге <имя_диска>:\WINNT\Microsoft.NET\Framework\<номер_версии>, как показано на рис. 1.4.

    Рис. 1.4. Библиотеки базовых классов .NET

    В зависимости от того, какие средства разработки вы используете для создания приложений .NET, существует много способов сообщить компилятору, какие именно сборки вы собираетесь задействовать во время процесса компиляции. Про эти способы будет рассказано ниже.

    Если вам стало немного не по себе от мысли о том, сколько информации о пространствах имен и о типах вам придется осваивать, помните, что помнить все типы всех пространств имен совершенно незачем. Если вы создаете консольное приложение, вам можно забыть о всех типах System.Windows.Forms и System.Drawing (а, скорее всего, и о многих других). Если же вы разрабатываете редактор изображений, вам вряд ли потребуются интерфейсы для доступа к базам данных. Типы пространств имен можно осваивать постепенно, по мере необходимости.

    Как получить дополнительную информацию о пространствах имен и типах

    Во всех главах этой книги мы будем осваивать различные возможности платформы .NET, используя пространства имен и содержащиеся в них типы. Книга вряд ли стала бы лучше, если бы мы рассмотрели в ней все без исключения типы во всех встроенных пространствах имен. Вы должны уметь находить информацию о нужных вам типах и пространствах имен самостоятельно, тем более что инструментов для этой цели много. Вот их краткий перечень:

  • документация .NET SDK (в MSDN);
  • утилита ILDasm.exe;
  • web-приложение ClassView;
  • графическое приложение WinCV;
  • ObjectBrowser, входящий в комплект Visual Studio.NET.

    Вряд ли вас нужно учить тому, как использовать MSDN (намекнем только, что внутри Visual Studio.NET можно попробовать начать с кнопки F1). А вот про все остальные утилиты стоит поговорить подробнее. Начнем с ILDasm.exe, ClassView и WinCV. Все эти утилиты поставляются вместе с .NET SDK.

    ILDasm.exe

    Официальное название ILDasm.exe звучит как Intermediate Language Disassembler utility (утилита дизассемблирования промежуточного языка). Эта утилита позволяет просмотреть содержимое любой сборки .NET (файла DLL или EXE) - ее манифест, метаданные типов и инструкции IL. При этом все операции производятся с использованием дружественного графического интерфейса. Просто запустите ILDasm.exe и через меню File 4 Open откройте нужную сборку. В порядке демонстрации мы откроем сборку mscorlib.dll (рис. 1.5). Путь к открытой нами сборке будет показан в заголовке окна ILDasm.

    Как мы видим, структура сборки представлена в самом обычном формате с деревом и узлами. Каждый метод, свойство, вложенный класс, как и все остальные типы, представлены специальными значками (в текстовом дампе дизассемблера эти значки будут заменены на аббревиатуры, состоящие из трех символов). Самые распространенные значки и соответствующие им аббревиатуры ILDasm приведены в табл. 1.4.

    Рис. 1.5. ILDasm.exe - ваш лучший друг в мире .NET

    Таблица 1.4. Условные обозначения в ILDasm

    Значки ILDasm Соответствующие аббревиатуры в текстовом дампе Значение
    (.dot) Показывает, что для типа может быть отображена дополнительная информация. В некоторых случаях двойной щелчок на этом значке позволяет перейти к связанному с ним узлу в дереве
    [NSP] Пространство имен
    [CLS] Класс. Вложенные классы представлены в формате <внешний_класс>$<внутренний_класс>
    [VCL] Структура
    [INT] Интерфейс
    [FLD] Поле (то есть открытые данные), определенное некоторым типом
    [STF] Статическое поле (то есть поле, которое принадлежит всем объектам данного класса)
    [MET] Метод
    [STM] Статический метод
    [PTY] Свойство

    Помимо просмотра информации о типах и их членах, ILDasm позволяет также получать информацию об инструкциях IL, относящихся к выбранному вами типу. В качестве примера можно найти и щелкнуть два раза мышью на значке конструктора по умолчанию для класса System.IO.BinaryWriter. Откроется отдельное окно, подобное представленному на рис. 1.6.

    Рис. 1.6. Просмотр кода IL в ILDasm

    Выгрузка в файл иерархии типов и членов сборки

    ILDasm обладает замечательной возможностью выгружать иерархию исследуемой вами сборки в текстовый файл. Потом, к примеру, полученный дамп можно изучать в любимой кофейне (или пивной). Чтобы создать текстовый дамп, откройте нужную сборку, в меню File выберите команду Dump TreeView и укажите имя для создаваемого текстового файла. Обратите внимание, что графические значки будут заменены соответствующими текстовыми аббревиатурами, как это показано в табл. 1.4. Пример текстового дампа представлен на рис. 1.7.

    Выгрузка в файл вместе с инструкциями IL

    В файл можно выгружать не только типы и члены типов исследуемой вами сборки, но и относящийся к объектам сборки код IL. Для этого в ILDasm предназначена другая команда: File 4 Dump. По умолчанию для дампов с инструкциями IL используется расширение *.il. На рис. 1.8 представлен код IL, относящийся к методу GetType() сборки mscorlib.dll (мы подробно рассмотрим этот метод в главе 7).

    Просмотр метаданных типов

    В ILDasm есть еще одна возможность, о которой обязательно следует упомянуть. С помощью ILDasm можно просматривать метаданные типов - ту информацию о типах сборки, которую генерирует .NET-совместимый компилятор для среды выполнения .NET. Для просмотра метаданных типов достаточно загрузить сборку в ILDasm и нажать клавиши Ctrl+M. Метаданные типов для приложения TestApp.exe (мы его вскоре создадим) представлены на рис. 1.9.

    Рис. 1.7. Текстовый дамп иерархии сборки, созданный ILDasm

    Рис. 1.8. Текстовый дамп сборки вместе с кодом IL

    Если вы еще не заметили сами, скажем, что ILDasm очень похож на утилиту OLE/COM Object Viewer. Oleview - это средство для получения информации о серверах COM и изучения кода IDL, который содержится в двоичных файлах COM. ILDasm - это средство для просмотра иерархии типов сборок .NET, связанного с ними кода IL и метаданных типов.

    Web-приложение ClassViewer

    Заглянуть внутрь сборок .NET можно с помощью еще одного средства - приложения ClassViewer. Это приложение входит в состав примеров .NET SDK. Для его запуска вам достаточно установить примеры .NET SDK, а затем в Internet Explorer открыть страницу по адресу http://localhost/ClassViewer/Default.aspx. ClassViewer позволит отслеживать отношения типов внутри сборок, используя web-интерфейс (рис. 1.10).

    Рис. 1.9. Просмотр метаданных типов в ILDasm

    Рис. 1.10. Просмотр типов в web-приложении ClassViewer

    Графическое приложение WinCV

    Последнее приложение, с которым мы познакомимся, называется WinCV.exe (от Windows Class Viewer). Это приложение позволяет просматривать определения типов C# в библиотеках базовых типов. Интерфейс этого приложения очень прост: наберите имя интересующего вас типа в строке поиска, и в окне Selected Class будут показаны его члены. На рис. 1.11 представлены члены класса System.Windos.Forms.ToolTip.

    Рис. 1.11. Окно WinCV

    Мы с вами рассмотрели приложения для просмотра сборок и типов C#. Следующая наша задача - познакомиться с теми средствами, которые используются для создания приложений C#.

    Создание приложений C# с использованием компилятора командной строки

    Создавать приложения C# можно с помощью компилятора командной строки csc.exe (C Sharp Compiler). Этот компилятор поставляется с .NET SDK, кроме того, его можно свободно загрузить с web-сайта Microsoft. В этом разделе мы используем этот компилятор для создания приложения на C#, которое будет называться TestApp.exe. Прежде всего, конечно, нам потребуется код этого приложения. Откройте текстовый редактор (вполне подойдет Блокнот), наберите в нем код, представленный на рис. 1.12, и сохраните полученный текстовый файл как TestApp.cs.

    Теперь наша задача - превратить этот исходный код в готовое приложение. При этом нам придется указать компилятору, какое именно приложение мы хотим получить на выходе - консольное (с расширением EXE), графическое Windows (опять-таки с расширением EXE), в виде модуля DLL или какое-либо другое. Для этого мы должны будем при компиляции указать в виде параметра командной строки нужный нам флаг. Перечень флагов компиляции для csc.exe представлен в табл. 1.5.

    Рис. 1.12. Класс TestApp

  • Начало
    Cодержание
    Отрывок
    [Заказать книгу в магазине "Мистраль"]

     

    VPS/VDS серверы. 30 локаций на выбор

    Серверы VPS/VDS с большим диском

    Хорошие условия для реселлеров

    4VPS.SU - VPS в 17-ти странах

    2Gbit/s безлимит

    Современное железо!

    Бесплатный конструктор сайтов и Landing Page

    Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

    SSD VPS в Нидерландах под различные задачи от 2.6$

    ✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

    🔥 Anti-DDoS защита 12 Тбит/с!

    Новости мира IT:

    Архив новостей

    IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

    Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
    тел. +7 495 7861149
    Пресс-релизы — pr@citforum.ru
    Обратная связь
    Информация для авторов
    Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
    Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
    Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...