This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Введение Разработка информационных систем, ориентированных на работу в среде Интернет в последнее время становится все более актуальной. Современные технологии позволяют создавать мощные, масштабируемые Web приложения, при этом, сам процесс создания таких приложений становится все более простым и доступным практически любому специалисту в области информационных систем. Наблюдается интеграция существующих технологий создания традиционных приложений, работающих на компьютере клиента, сетевых приложений, ориентированных на выполнение мощными серверами, обслуживающими тысячи клиентов и средств проектирования приложений. Технологии разработки приложений, созданные корпорацией Microsoft по праву считаются одними из наиболее современных и передовых. Они отвечают всем современным требованиям, предъявляемым сегодня к средствам разработки профессиональных приложений. Одной из наиболее современных и актуальных технологий такого рода является технология разработки Интернет приложений ASP.NET. Ее преимуществом можно считать гибкую архитектуру, простоту, использование широко распространенных языков программирования и унифицированной технологии доступа к данным. Настоящий курс посвящен рассмотрению основ создания интернет приложений на основе ASP.NET с использованием языка программирования C#. В качестве среды, используемой для разработки выбрана Visual Studio 2005. Курс предназначен для изучения базовых принципов разработки интернет приложений. Для его успешного усвоения необходимо понимание основ программирования, знания основных принципов работы Web приложений, синтаксиса языка HTML, а также начальные сведения об архитектуре .NET Framework. Помимо рассмотрения основных моментов, связанных с расширением представления читателя относительно перечисленных выше вопросов, рассматриваются принципы работы в среде Visual Studio 2005, архитектура приложений ASP.NET, использование мастер страниц, а также принципов построения интерфейса пользователя, использование различных средств, облегчающих создание и поддержание многостраничных Web приложений, а также основы работы с базами данных. Курс может быть полезен всем желающим начать изучение принципов построения Интернет приложений с использованием ASP.NET, студентам специальностей Прикладная информатика, автоматизированные систему обработки информации и управления, системы автоматизированного проектирования.
2
Занятие 1.Принципы работы и структура Web приложений на основе ASP.NET Архитектура Web приложений Web приложения представляют собой особый тип программ, построенных по архитектуре «клиент-сервер». Особенность заключается в том, что само Web приложение находится и выполняется на сервере, клиент при этом получает только результаты работы. Работа приложения основывается на получении запросов от пользователя (клиента), их обработке и выдачи результата. Передача запросов и результатов их обработки происходит через Интернет (рис.1.1).
Рис. 1.1. Архитектура Web приложения. Слайд 1.
Отображением результатов запросов, а также приемом данных от клиента и их передачей на сервер обычно занимается специальное приложение – браузер (Internet Expolrer, Mozilla, Opera и т.д.). Как известно, одной из функций браузера является отображения данных, полученных из Интернета в виде страницы, описанной на языке HTML, следовательно, результат, передаваемый сервером клиенту должен быть представлен на этом языке. На стороне сервера Web приложение выполняется специальным программным обеспечением (Web сервером), который и принимает запросы клиентов, обрабатывает их, формирует ответ в виде страницы, описанной на языке HTML и передает его клиенту. Одним из таких Web серверов является Internet Information Services (IIS) компании Microsoft. Это единственный Web сервер, способный выполнять Web приложения, созданные с использованием технологии ASP.NET. В процессе обработки запроса пользователя, Web приложение компонует ответ на основе исполнения программного кода, работающего на
3
стороне сервера, Web формы, страницы HTML, другого содержимого включая графические файлы. В результате, как уже было сказано, формируется HTML страница, которая и отправляется клиенту. Получается, что результат работы Web приложения идентичен результату запроса к традиционному Web сайту, однако, в отличие от него, Web приложение генерирует HTML код в зависимости от запроса пользователя, а не просто передает его клиенту в том виде, в котором этот код хранится в файле на стороне сервера. То есть, Web приложение динамически формирует ответ с помощью исполняемого кода, так называемой исполняемой части. За счет наличия исполняемой части, Web приложения способны выполнять практически те же операции, что и обычные Windows приложения, с тем лишь ограничением, что код исполняется на сервере, в качестве интерфейса системы выступает браузер, а в качестве среды, посредством которой происходит обмен данными – Интернет. К наиболее типичным операциям, выполняемым Web приложениями относятся: • прием данных от пользователя и сохранение их на сервере; • выполнение различных действий по запросу пользователя: извлечение данных из базы данных (БД), добавление, удаление, изменение данных в БД, проводить сложные вычисления; • аутентифицировать пользователя и отображать интерфейс системы, соответствующий данному пользователю; • отображать оперативную постоянно изменяющуюся информацию • и т.д. Краткое описание архитектуры ASP.NET и .NET Framework ASP.NET – это платформа для создания Web-приложений и Webсервисов, работающих под управлением IIS. Сегодня существуют другие технологии, позволяющие создавать Web-приложения. К ним относятся прежде всего, очень популярные сегодня языки PHP и PERL, более старая и менее популярная сегодня технология CGI и т.д. Однако, ASP.NET отличается от них высокой степенью интеграции с серверными продуктами, а также с инструментами Microsoft для разработки, доступа к данным и обеспечения безопасности. Кроме того, использование ASP.NET позволяет разрабатывать Web и Windows приложения используя очень похожие технологические цепочки, одинаковые языки программирования, технологии доступа к данным и т.д. Более того, базовые языки программирования, с помощью которых сегодня возможна разработка Web приложений являются полностью объектно-ориентированными, что делает разработку исполнимой части, а также ее модификацию, обслуживание, отладку и повторное использование гораздо более простым занятием, чем в других технологиях. Существует достаточно большой перечень сильных сторон использования ASP.NET для создания сложных Web приложений. Целью данного курса не является описание всех сильных и слабых сторон этой платформы. Более подробно об этом можно узнать на сайте www.microsoft.com, а также в [1].
4
Заметим лишь, что ASP.NET функционирует исключительно на серверах Windows, так как требует наличия IIS. Для создания Webприложений, не требующих IIS, а использующих, например, Web сервер Apache и работающих на серверах под управлением операционных систем, отличных от Windows, применяются другие технологии. Важным моментом в понимании архитектуры ASP.NET является тот факт, что она является частью инфраструктуры .NET Framework. Более подробно об архитектуре и принципах работы .NET Framework можно узнать в [2]. Так как эта тема является слишком объемной и выходит за рамки данного курса, ограничимся лишь кратким описанием инфраструктуры .NET Framework. Архитектура .NET Framework Как утверждает корпорация Microsoft, до 80% средств, направленных на исследования и разработки, тратится на платформу .NET и связанные с ней технологии. Результаты такой политики на сегодняшний день выглядят впечатляюще. Так, область охвата платформы .NET просто огромна. Платформа состоит из четырех групп программных продуктов: набор языков, куда входят С# и Visual Basic .NET; набор инструментальных средств разработки, в том числе Visual Studio .NET; обширная библиотека классов для построения веб-служб и приложений, работающих в Windows и в Интернете; а также среда выполнения программ CLR (Common Language Runtime, общеязыковая среда выполнения), в которой выполняются объекты, построенные на этой платформе; набор серверов .NET Enterprise Servers, ранее известных под именами SQL Server 2000, Exchange 2000, BizTalk 2000 и др., которые предоставляют специализированные функциональные возможности для обращения к реляционным базам данных, использования электронной почты, оказания коммерческих услуг «бизнес-бизнес» (В2В) и т. д.; богатый выбор коммерческих веб-служб, называемых .Net My Services. За умеренную плату разработчики могут пользоваться этими службами при построении приложений, требующих идентификации личности пользователя и других данных; новые некомпьютерные устройства, поддерживающие средства .NET, от сотовых телефонов до игровых приставок. Microsoft .NET поддерживает не только языковую независимость, но и языковую интеграцию. Это означает, что разработчик может наследовать от классов, обрабатывать исключения и использовать преимущества полиморфизма при одновременной работе с несколькими языками. Платформа .NET Framework предоставляет такую возможность с помощью спецификации Common Type System (CTS, общая система типов), которая полностью описывает все типы данных, поддерживаемые средой выполнения, определяет, как одни типы данных могут взаимодействовать с другими и как они будут представлены в формате метаданных .NET. Например, в .NET любая сущность является объектом какого-нибудь класса,
5
производного от корневого класса System.Object. Спецификация CTS поддерживает такие общие понятия, как классы, делегаты (с поддержкой обратных вызовов), ссылочные и размерные типы. Важно понимать, что не во всех языках программирования .NET обязательно должны поддерживаться все типы данных, которые определены в CTS. Спецификация Common Language Specification (CLS, общая языковая спецификация) – устанавливает основные правила, определяющие законы, которым должны следовать все языки, такие как ключевые слова, типы, примитивные типы, перегрузки методов и т.п. Спецификация CLS определяет минимальные требования, предъявляемые к языку платформы .NET. Компиляторы, удовлетворяющие этой спецификации, создают объекты, способные взаимодействовать друг с другом. Любой язык, соответствующий требованиям CLS, может использовать все возможности библиотеки FCL (Framework Class Library, библиотека классов платформы). CLS позволяет и разработчикам, и поставщикам, и производителям программного обеспечения не выходить за пределы общего набора правил для языков, компиляторов и типов данных. Платформа .NET Framework является надстройкой над операционной системой, в качестве которой может выступать любая версия Windows1. На сегодняшний день платформа .NET Framework включает в себя: • четыре официальных языка: С#, VB.NET, Managed C++ (управляемый C++) и JScript .NET; • объектно-ориентированную среду CLR (Common Language Runtime), совместно используемую этими языками для создания приложений под Windows и для Internet; • ряд связанных между собой библиотек классов под общим именем FCL (Framework Class Library). Отношения с концептуальной точки зрения архитектурных компонентов платформы .NET Framework представлены на рис.1.2Ошибка! Неверная ссылка закладки..
1
Благодаря архитектуре среды CLR в качестве операционной системы может выступать любая версия Unix и вообще любая ОС.
6
Рис. 1.2. Архитектура .NET Framework. Слайд 2
Самым важным компонентом платформы .NET Framework является CLR(Common Language Runtime), предоставляющая среду, в которой выполняются программы. Главная ее роль заключается в том, чтобы обнаруживать и загружать типы .NET и производить управление ими в соответствии с полученными командами. CLR включает в себя виртуальную машину, во многих отношениях аналогичную виртуальной машине Java. На верхнем уровне среда активизирует объекты, производит проверку безопасности, размещает объекты в памяти, выполняет их, а также запускает сборщик мусора. Под сборкой мусора понимается освобождение памяти, занятой объектами, которые стали бесполезными и не используются в дальнейшей работе приложения. В ряде языков программирования (например, C/C++) память освобождает сам программист, в явной форме отдавая команды как на создание, так и на удаление объекта. В этом есть своя логика – "я тебя породил, я тебя и убью". Однако в CLR задача сборки мусора (и другие вопросы, связанные с использованием памяти) решается в нужное время и в нужном месте исполнительной средой, ответственной за выполнение вычислений. На рис.1.2 над уровнем CLR находится набор базовых классов платформы, а над ним расположены слой классов данных и XML, а также слой классов для создания веб-служб (Web Services), веб- и Windowsприложений (Web Forms и Windows Forms). Собранные воедино, эти классы известны под общим именем FCL (Framework Class Library). Это одна из самых больших библиотек классов в истории программирования. Она окрывает доступ к системным функциям, включая и те, что прежде были доступны только через API Windows, а также к прикладным функциям для Web-разработки (ASP.NET), для доступа к данным (ADO.NET), обеспечения
7
безопасности и удаленного управления. Имея в своем составе более 4000 классов, библиотека FCL способствует быстрой разработке настольных, клиент-серверных и других приложений и веб-служб. Набор базовых классов платформы – нижний уровень FCL – не только прячет обычные низкоуровневые операции, такие как файловый ввод-вывод, обработка графики и взаимодействие с оборудованием компьютера, но и обеспечивает поддержку большого количества служб, используемых в современных приложениях (управление безопасностью, поддержка сетевой связи, управление вычислительными потоками, работа с отражениями и коллекциями и т.д.). Над этим уровнем находится уровень классов, которые расширяют базовые классы с целью обеспечения управления данными и XML. Классы данных позволяют реализовать управление информацией, хранящейся в серверных базах данных. В число этих классов входят классы SQL (Structured Query Language, язык структурированных запросов), дающие программисту возможность обращаться к долговременным хранилищам данных через стандартный интерфейс SQL. Кроме того, набор классов, называемый ADO.NET, позволяет оперировать постоянными данными. Платформа .NET Framework поддерживает также целый ряд классов, позволяющих манипулировать XML-данными и выполнять поиск и преобразования XML. Базовые классы, классы данных и XML расширяются классами, предназначенными для построения приложений на основе трех различных технологий: Web Services (веб-службы), Web Forms (веб-формы) и Windows Forms (Windows-формы). Веб-службы включают в себя ряд классов, поддерживающих разработку облегченных распределяемых компонентов, которые могут работать даже с брандмауэрами и программами трансляции сетевых адресов (NAT). Поскольку веб-службы применяют в качестве базовых протоколов связи стандартные протоколы HTTP и SOAP, эти компоненты поддерживают в киберпространстве подход «plug-and-play». Инструментальные средства Web Forms и Windows Forms позволяют применять технику Rapid Application Development (RAD, быстрая разработка приложений) для построения веб- и Windows-приложений. Эта техника сводится к перетаскиванию элементов управления с панели инструментов на форму, двойному щелчку по элементу и написанию кода, обрабатывающего события, связанные с этим элементом. Компиляция и язык MSIL .NET-приложения исполняются иначе, чем традиционные Windowsприложения. Такие программы компилируются фактически в два этапа. На первом этапе исходный код компилируется во время построения проекта и вместо исполняемого файла с машинными кодами получается сборка2 2
Сборка (assembly) – это коллекция файлов, которая предстает перед программистом в виде единой библиотеки динамической компоновки(DLL) или исполняемого файла (EXE)/ в технологии .NET сборка является базовой единицей для повторного использования, контроля версий, защиты и развертывания. Среда CLR представляет программисту ряд классов, позволяющих манипулировать сборками.
8 (assembly), содержащая команды промежуточного языка Microsoft Intermediate Language – (MSIL, промежуточный язык Microsoft). Код IL сохраняется в файле
на диске. При этом, файлы MSIL (сокращенно IL), генерируемые компилятором, например, С#, идентичны IL-файлам, генерируемым компиляторами с других языков .NET. В этом смысле платформа остается в неведении относительно языка. Самой важной характеристикой среды CLR является то, что она общая; одна среда выполняет как программы, написанные на С#, так и программы на языке VB.NET. Второй этап компиляции наступает непосредственно перед фактическим выполнением страницы. На этом этапе CLR транслирует промежуточный код IL в низкоуровневый собственный машинный код, выполняемый процессором. Процесс происходит следующим образом: при выполнении .NET-программы системы CLR активизирует JIT-компилятор, который затем превращает MSIL во внутренний код процессора. Этот этап известен как оперативная компиляция «точно к нужному моменту» (Just-In-Time) или JIT-компиляция (JIT'ing) и он проходит одинаково для всех приложений .NET(включая, например, приложения Windows). При исполнении программы CLR берет на себя управление памятью, контроль типов и решает за приложение ряд других задач. На рис.1.3 показан этот двухэтапный процесс компиляции.
Рис. 1.3. Схема компиляции .NET-приложения.
9
Стандартный JIT-компилятор работает по запросу. Когда вызывается тот или иной метод, JIT-компилятор анализирует IL-код и производит высокоэффективный машинный код, выполняемый очень быстро. JITкомпилятор достаточно интеллектуален, чтобы распознать, был ли код уже скомпилирован, поэтому во время выполнения программы компиляция происходит лишь при необходимости. По ходу своего выполнения .NETпрограмма работает все быстрее и быстрее, так как повторно используется уже скомпилированный код. Спецификация CLS подразумевает, что все языки платформы .NET генерируют очень похожий IL-код. Кроме того, при компилировании программы в дополнение к MSIL формируется еще один компонент – метаданные, которые описывают данные, используемые программой, что позволяет коду взаимодействовать с другим кодом. В результате объекты, созданные на одном языке, доступны и могут наследоваться на другом. То есть можно создать базовый класс на языке VB.NET, а производный от него класс – на языке С#. В целом, при написании приложения создается так называемый управляемый код (managed code), который выполняется под контролем не зависящей от языка среды исполнения CLR приложения. Поскольку приложение запускается под контролем CLR, управляемый код должен соответствовать определенным требованиям (т.е. компилятор должен создать MSIL-файл, предназначенный для CLR, а также использовать библиотеки .Net Framework3), при выполнении которых он получает множество преимуществ, включая современное управление памятью, способность совмещать языки, высокий уровень безопасности передачи данных, поддержку контроля версии и понятный способ взаимодействия компонентов программного обеспечения4. Таким образом, компиляция .NET делится на два этапа с целью предоставления разработчикам удобных условий и мобильности. Перед созданием низкоуровневого машинного кода компилятору необходимо знать, в какой операционной системе и на каком базовом оборудовании будет функционировать приложение. Благодаря двум этапам компиляции можно создать скомпилированную сборку с кодом .NET и распределить ее более чем на одну платформу. Конечно, компиляция не будет столь полезна, если ее выполнение будет необходимо каждый раз при запросе пользователем Web-страницы. К счастью, приложения ASP.NET не нужно компилировать всякий раз при запросе Web-страницы или Web-службы. Вместо этого код IL создается один раз и повторно генерируется только при изменении исходного кода. Подобным образом файлы собственного машинного кода кэшируются в 3
Библиотека классов .NET открывает доступ ко всем возможностям CLR. Классы, составляющие эту библиотеку, организованы при помощи пространств имен. Каждое пространство имен заключает в себе классы, выполняющие близкие функции. 4 Альтернативой управляемому коду является не управляемый код, который не выполняется CLR. Однако, до появления .NET Framework все Windows-программы использовали именно не управляемый код.
10
системном
каталоге
с
путем
вроде С:\[WinDir]\Microsoft.NET\Framework\[Version]\Temporary ASP.NET Files, где WinDir является каталогом Windows, a Version – номером установленной в данный момент версии .NET. Архитектура ASP.NET Каждое Web приложение, разрабатываемое на основе ASP.NET состоит из информационной части, программного кода, и сведений о конфигурации. Информационная часть содержит статические и динамические элементы страницы и реализуются в виде Web форм. Статические элементы представляют собой типичные элементы языка HTML, динамические же компонуются программным кодом приложения во время его выполнения (например, запросы к базе данных). Программный код реализует логику, определенную в процедурах обработки данных, определяющих реакцию приложения на запросы пользователя. Программный код исполняется сервером и взаимодействует с динамическими элементами информационной части для формирования отклика приложения. Сведения о конфигурации представляют собой файлы, содержащие параметры, определяющие способ исполнения приложения на сервере, параметры безопасности, реакцию приложения на возникающие ошибки и т.д. Основным элементом Web приложения является Web форма (или Web страница), которая с одной стороны похожа на Windows форму, т.к. позволяет размещать внутри себя различные элементы управления, способные отображать данные и реагировать на действия пользователя, а с другой – представляет собой HTML страницу, т.к. содержит все ее атрибуты. Описания элементов управления, упомянутых ранее представляются в коде HTML страницы в виде специальных тегов. На рис.1.4 представлен пример простейшей страницы Web приложения, содержащего всего лишь один элемент – кнопку. Как видно из рисунка, основой страницы является тело стандартного HTML документа, внутри которого находится элемент form, а также кнопка button. Кроме того, здесь присутствуют некоторые дополнительные элементы вначале документа, которые будут рассмотрены позднее.
11 Дополнительные элементы
Тело HTML документа
Рис. 1.4. Пример простейшей страницы Web приложения
При запуске приложения данная страница отображается в окне браузера и выглядит следующим образом (рис.1.5).
Рис. 1.5. Отображение страницы Web приложения в окне браузера
В свою очередь, с кнопкой связан программный код, который выполняется при нажатии на нее. Этот код располагается в отдельном файле, окно которого в момент разработки изображено на рис.1.6.
12
Рис. 1.6. Файл, содержащий программный код Web страницы
На самом деле, при разработке Web приложений на основе ASP.NET возможны два варианта организации Web форм. В первом случае весь код информационной части и программная часть хранятся в одном файле с расширением .aspx. Программный код при этом помещается в так называемые блоки сценариев. При этом, сохраняется возможность использования всех принципов современного программирования, таких как реакция на события элементов управления, подпрограммы и т.д. Такую модель целесообразно использовать при создании простых Web приложений, поскольку в этом случае все хранится в одном пакете. Во втором случае каждая Web страница разделяется на две части: Web форму и файл, содержащий программный код. При этом, форма, как и в первом случае, сохраняется в файле с расширением .aspx, а программный код – с расширением .cs. Такая модель обеспечивает лучшую организацию элементов Web приложения за счет отделения пользовательского интерфейса от программной логики. В примере, рассмотренном ранее, Web страница разделена на две части, при этом форма и программный код хранятся в разных файлах. В следующем примере, изображенном на рис.1.7 показана аналогичная предыдущей Web страница, в которой форма и программный код объединены в одном файле.
13
Рис. 1.7. Пример Web формы, содержащей описание формы и программный код в одном файле
Изучив этот пример, можно описать типовой сценарий взаимодействия элементов Web приложения друг с другом и с клиентом, осуществляющим запрос формы этого приложения (рис.1.8).
Рис. 1.8. Типовой сценарий взаимодействия элементов Web приложения с клиентом.
Как видно из рис.1.8, при обращении клиента к Web приложению, последнее запускается на сервере IIS. Запущенное приложение формирует отклик. Для этого на сервере создается экземпляр запрошенной Web формы, которая генерирует HTML текст отклика, который и передается браузеру
14
клиента. Сразу после этого экземпляр Web формы уничтожается. Пользователь, получив HTML страницу, сгенерированную приложением, имеет возможность заполнять различные поля формы (тестовые поля, переключатели и т.п.). После заполнения всех необходимых полей формы, пользователь инициирует отправку данных, введенных им в страницу обратно на сервер. Это происходит за счет использования технологии обратной отсылки, которая вызывается при выполнении определенных действий (например, нажатия на кнопку). Получив данные от пользователя, сервер создает новый экземпляр Web формы, заполняет его полученными данными и обрабатывает все необходимые события. По окончании обработки, сервер формирует HTML код ответа и отправляет его клиенту, а затем уничтожает экземпляр Web формы. Более подробно описанный сценарий изображен на рис.1.9 и 1.10.
Запрос URL (HTTP GET)
Создание Web страницы на основе дескрипторов из файла .aspx запрос Выполнение пользовательского кода инициализации
Сгенерированный HTML код
Сериализация динамической информации о состоянии вида ответ Генерация HTML кода для объектов страницы
Рис. 1.9. Подробный сценарий взаимодействия элементов Web приложения с клиентом при первом запросе.
15
Отсылка
страницы
по
Создание Web страницы на основе дескрипторов из файла .aspx
URL (HTTP POST) Десериализация и применение данных состояния вида
Выполнение пользовательского кода инициализации
запрос
Выполнение пользовательского кода обработки событий
Сгенерированный HTML код
Сериализация динамической информации о состоянии вида ответ Генерация HTML кода для объектов страницы
Рис. 1.10. Подробный сценарий взаимодействия элементов Web приложения с клиентом при запросе обратной отсылки.
В момент окончания работы с Web приложением, пользователь либо закрывает браузер, либо переходит на другую интернет страницу. В этот момент завершается сеанс работы пользователя с данным приложением, однако само приложение может быть завершено сервером не сразу после окончания последнего сеанса работы пользователя. Это связано с управлением распределением памяти платформой .NET Framework, которая основана на периодической проверке ссылок объектов. Если в результате такой проверки обнаружится, что объект больше не используется, сервер уничтожает его, освобождая таки образом занимаемую им память. Таким образом, нельзя точно сказать когда именно наступит событие Application_End для данного Web приложения. Такой принцип организации выполнения Web приложений хорошо подходит для масштабируемых приложений с интенсивным сетевым обменом. Однако у него есть и недостатки. В частности, оказывается невозможным сохранять данные, принадлежащие форме даже в течение работы пользователя с приложением. Т.е. если мы захотим создать некую переменную, хранящую, например идентификатор заказа, с которым мы в данный момент работаем, сделать это будет невозможно, т.к. форма после
16
отправки клиенту сразу же уничтожается. Чтобы обойти этот недостаток, ASP.NET использует специальный механизм для сохранения данных, введенных в элементы управления Web формы. Согласно этому принципу, в рамках каждого запроса, на сервер отправляются все данные, которые были введены в элементы управления. При этом, как уже упоминалось выше, на сервере возникает событие Page_Init, целью которого является создание Web формы и ее инициализация. В процессе инициализации, в элементы управления созданной формы записываются переданные от клиента данные. Теперь эти данные становятся доступны приложению посредством обработки события Page_Load, возникающего при каждом обращении к странице. Вообще, для лучшего понимания процесса взаимодействия пользователя с Web приложением, рассмотрим последовательность событий, возникающую при обращении клиента к странице приложения. Итак, при запросе страницы, прежде всего, инициируется событие Page_Init, которое производит начальную инициализацию страницы и ее объекта. В рамках обработки данного события программист может разместить код, осуществляющий начальную инициализацию страницы. Тем не менее, это событие нельзя использовать для инициализации элементов управления, размещенных на странице, так как они еще не созданы. После этого инициируется событие Page_Load. Большинство Web страниц используют это событие для выполнения инициализации, например заполнение полей данными, установка начальных значений для элементов управления и т.д. Кроме того, в процедуре обработки данного события возможно определение того была ли загружена страница впервые, или обращение к ней осуществляется повторно в рамках технологии обратной отсылки, произошедшей в результате нажатия пользователем кнопки, либо другого элемента управления, размещенного на странице. В английской терминологии обратная отсылка данных на сервер называется post back. Для определения текущего состояния страницы необходимо проверить свойство Page.IsPostBack, которое будет иметь значение false при первом запуске страницы. Определение того, производится ли первое обращение к данной странице, либо повторное важно, так как позволяет производить инициализацию только в том случае, когда страница запрашивается впервые. Так, например, при обратной отсылке данных на сервер не только нет необходимости производить инициализацию устанавливая начальные значения элементов управления, это может быть ошибкой, так как эти элементы управления должны получить значения, переданные им от пользователя. В дальнейшем, в случае, если для страницы была произведена обратная отсылка, вызываются события элементов управления, размещенных на странице. Эти события запоминаются в тот момент, когда пользователь производил действия с элементами управления в окне браузера, а при передаче данных на сервер исполняются по порядку. После вызова события Page_Load происходит так называемая проверка достоверности страницы. Необходимость такой проверки возникает тогда, когда пользователь ввел в элементы управления, расположенные на странице
17
данные, которые впоследствии необходимо сохранить или использовать для обработки. В идеале проверка достоверности должна происходить на стороне клиента для того, чтобы пользователь был проинформирован о проблемах с вводом данных перед их отправкой на сервер, т.к. это позволяет уменьшить объем информации, передаваемой по сети и ускорить процесс обмена данными с сервером. Однако, независимо от того, была ли произведена проверка достоверности данных на стороне клиента или нет, ее необходимо осуществлять и на стороне сервера. Осуществление проверка достоверности достаточно сложная задача. Сложность эта обусловлена различием моделей клиентского и серверного программирования. В ASP.NET существует несколько элементов управления проверкой достоверности. Эти элементы управления выполняют автоматическую клиентскую и серверную проверку достоверности. В случае, если проверка достоверности выявила ошибки во введенных данных, ASP.NET уведомит об этом пользователя и не позволит осуществлять дальнейшую работу со страницей до устранения ошибок. Более подробно организация проверки достоверности рассматривается в лекции 5. Следующим шагом обработки Web формы является обработка всех событий, инициированных пользователем с момента последней обратной отсылки. Для иллюстрации этого, приведем пример. Пусть у нас существует страница с кнопкой (Button) «Отправить» и текстовым полем (TextBox) без автоматической обратной отсылки. При изменении текста в текстовом поле и щелчке на кнопке «Отправить» инициируется обратная отправка данных страницы на сервер (этого не произошло при изменении текста в текстовом поле, так как соответствующая опция этого элемента управления AutoPostBack установлена в false). В момент обратной отправки страницы на сервер ASP.NET запускает следующие события: 1. 2. 3. 4. 5. 6.
В результате обработки всех инициированных событий, генерируется HTML код страницы, который и передается клиенту, после чего выполняется Очистка, в рамках которой, инициируется событие Page_Unload. Оно предназначено для освобождения ресурсов, занятых данной страницей. Событие Page.PreRender инициируется после того, как сервер обработал все события страницы, но генерация ее HTML кода еще не произошла. Обычно это событие используется ASP.NET для привязки элементов управления к источникам данных непосредственно перед созданием HTML кода и отправкой его клиенту. Описанная последовательность событий позволяет создать описание жизненного цикла Web страницы, изображенного на рис.1.11.
18
2
Инициализация структуры страницы
3
Инициализация кода пользователя
4
Проверка достоверности
5
Обработка событий
1
Запрос браузера
7
Ответ браузеру
6 8
Очистка
Рис. 1.11. Жизненный цикл страницы ASP.NET
Вернемся, однако, к проблеме сохранения данных страницы в промежутке между обращениями к ней. Для реализации этого механизма в ASP.NET используются состояния отображения (view state). Состояние отображения Web формы доступно только внутри этой Web формы. Если необходимо сделать данные, введенные в данную Web форму доступными другим Web формам одного и тоге же приложения, их необходимо сохранить в объектах с более глобальной областью видимости, которые называют переменными состояния. Объектов переменных состояний два: Application и Session. Переменные состояния Application доступны всем пользователям приложения и могут рассматриваться как глобальные переменные, обращение к которым возможно из любых сеансов. Переменные состояния Session доступны только в рамках одного сеанса, следствием чего является то, что они оказываются доступными только одному пользователю. В переменных состояния можно хранить данные любого типа. В силу того, что переменные состояния являются фактически глобальными переменными, для работы с ними, желательно выработать стратегию их использования в приложении. Более подробно работа с состояниями отображения и переменными состояния будет рассмотрена в разделе класс Page занятия 2.
19
Занятие 2.Основы работы в Visual Studio.NET 2005 Почти все крупномасштабные Web-сайты ASP.NET разрабатываются с использованием Visual Studio – предлагаемой компанией Microsoft полнофункциональной среды разработки Web-приложений, гибкого и универсального инструмента проектирования и создания законченных приложений для платформы windows. Работа над ней шла многие годы, и в нее включены многие функции, специфические для .NET Framework. Как и любая другая профессиональная среда разработки, Visual Studio включает в себя средства управления проектами, редактор исходного текста, конструкторы пользовательского интерфейса, мастера, компиляторы, компоновщики, инструменты, утилиты, документацию и отладчики. Она позволяет создавать приложения для 32- и 64-разрядных Windows-платформ, а также новой платформы .NET Framework. Одно из важнейших усовершенствований – возможность работы с разными языками и приложениями различных типов в единой среде разработки. К другим основным преимуществам Visual Studio можно отнести следующие: Встроенный редактор WISIWYG («Что видишь, то и • получаешь»). С его помощью Visual Studio позволяет настраивать статическое HTML-содержимое, в том числе шрифты и стили. Меньше кода для написания путем автоматизации базовых • задач по созданию стереотипного кода. Например, при добавлении на Web-страницу нового элемента управления необходимо также определить переменную, позволяющую манипулировать этим элементом управления в коде, что и выполняет Visual Studio. Похожая автоматизация применяется и при подключении к Webслужбам. Интуитивный стиль программирования – форматирование • кода, выполняемое Visual Studio, во время его набора в виде автоматической расстановки отступов и использования цветового кодирования, значительно улучшает удобочитаемость кода и снижает вероятность допущения ошибок в коде. Встроенный Web-сервер, позволяющий запускать Web• сайт прямо из среды проектирования, что, кроме удобства, увеличивает степень безопасности за счет отсутствия возможности получения доступа с внешнего компьютера к разрабатываемому Web-сайту. Многоязыковая разработка – Visual Studio позволяет • кодировать на любых языках с использованием одного и того же
20
интерфейса (IDE). Более того, эта среда разработки предоставляет возможность помещения Web-страниц, реализованных на C#, в тот же проект, в котором уже имеются Web-страницы, написанные на Visual Basic. Единственное ограничение: на одной странице нельзя применять более одного языка. Увеличение скорости разработки, для чего Visual Studio • обладает множеством возможностей. Удобства вроде мощной функции поиска и замены, а также средств автоматического добавления и удаления комментариев, которые могут временно скрыть блок кода, позволяют работать быстро и эффективно. Отладка, для удобства осуществления которой Visual • Studio располагает некоторым инструментарием. Так, возможно выполнять код по строке за раз, устанавливать интеллектуальные точки прерывания, которые можно сохранить для дальнейшего использования, и в любое время отображать текущую информацию из памяти. Это неполный перечень всех возможностей данной среды разработки, предоставляющей разработчикам поистине огромный арсенал возможностей. Начало работы с Visual Studio 2005 При загрузке Visual Studio 2005 отображается окно, состоящее из нескольких областей (рис.2.1). Область окна Recent Projects содержит ссылки на 6 последних проектов, открытых в Visual Studio. Размещенная под ней область Getting Started содержит ссылки на наиболее часто возникающие у пользователя задачи. Щелчок по такой ссылки приведет к немедленному выполнения данной задачи. Например, щелчок по ссылке Create Web Site приведет к немедленному созданию нового Web сайта на основе ASP.NET. В центре окна Visual Studio 2005 располагается область, в которой отображаются (при условии подключения к Интернет) последние опубликованные в MSDN статьи, посвященные тем, технологиям, которые применяются для разработки на данном компьютере. В данном случае изображен раздел MSDN: ASP.NET, т.к. именно ASP.NET применяется чаще всего на данном компьютере для создания приложений. Вся область окна, задействованная для отображения перечисленных выше разделов в дальнейшем используется для отображения содержимого редактируемого документа (файла) и называется окном документов.
21
Рис. 2.1. Главное окно интегрированной среды разработки приложений Visual Studio 2005.
Правая часть окна Visual Studio 2005 занята так называемыми инструментальными окнами. В них отображаются компоненты приложений: элементы управления, соединения с базами данных, классы и свойства, используемые в проектах. Инструментальные окна можно настроить по своему усмотрению для максимального удобства использования в процессе работы. Их можно перетаскивать, располагая в любом месте экрана (Рис. 2.2).
22 Кнопка Auto Hide
Скрытое инструментальное окно
Рис. 2.2. Произвольное расположение инструментальных окон в интегрированной среде разработки Visual Studio 2005.
Инструментальные окна можно также настраивать таким образом, чтобы они постоянно находились на экране. Для этого необходимо включить кнопку Auto Hide, расположенную в правом верхнем углу окна (рис. 2.2). С помощью данной кнопки можно добиться того, чтобы окно либо постоянно находилось на экране, либо скрывалась за боковой границей основного окна в случае, если курсор мыши вышел за его пределы. Важным компонентом интерфейса является также способ отображения окон документов внутри интегрированной среды разработки. Существует два способа отображения окон: стандартный многооконный интерфейс, используемый многими программами, когда внутри основного окна создается новое окно, которое представляет собой отдельное окно с заголовком, но которое не может быть вынесено за пределы главного окна и интерфейс на основе закладок, когда окно документа всегда занимает все рабочее пространство внутри главного окна, а переключение между окнами возможно путем щелчка по соответствующей вкладке (Рис. 2.3).
23
Вкладки, соответствующие окнам
Рис. 2.3. Расположение окон интегрированной среды разработки с помощью вкладок.
Для переключения режимов отображения окон, а также установки дополнительных настроек, связанных с отображением окон в Visual Studio 2005 необходимо выполнить команду Tools → Options и в открывшемся окне Options на вкладке General установить опцию Tabbed documents. Переключение режимов отображения окон
Рис. 2.4. Окно настроек свойств интегрированной среды разработки.
24
Создание нового приложения Для создания нового приложения ASP.NET следует выполнить команду File Ö New Ö Web Site.
*
Следует обратить внимание, что команда New Ö Project не используется, поскольку Web-приложения не являются проектами
В открывшемся окне New Web Site (Рис. 2.5) можно указать следующие сведения: − шаблон (Templates) – определяет файлы, с которых будет начинаться Web-сайт. Поддерживаются два типа базовых приложений ASP.NET – приложения Web-сайтов и приложения Web-служб. Шаблоны этих приложений были скомпонованы и откомпилированы аналогично. Фактически возможно добавление Web страницы к приложению Web-службы, а также Web-службы к обычному Webприложению. Различие заключается в файлах, создаваемых Visual Studio по умолчанию. В Web-приложении работа начинается с образца Web-страницы, в приложении Web-службы – с образца Web-службы. Кроме того, имеются более сложные шаблоны для определенных типов сайтов. Можно также создавать собственные шаблоны или загружать готовые шаблоны сторонних поставщиков; − месторасположение (Location) – определяет место хранения файлов Web-сайта. Обычно это File System – Файловая Система (указывается папка на локальном компьютере либо сетевой путь). Но возможно также редактирование сайта непосредственно по HTTP или FTP протоколу; − язык (Language) – определяет язык программирования .NET, используемый по умолчанию для кодирования Web-сайта.
25
Рис. 2.5. Окно создания нового Web приложения.
Альтернативой ручному вводу месторасположения является нажатие кнопки Обзор (Browse), отображающей диалоговое окно Выбор месторасположения (Choose Location, рис. 2.6).
Выбор местоположения создания нового Web приложения
Рис. 2.6. Установка месторасположения создаваемого Web приложения.
Вдоль левой стороны диалогового окна расположены четыре кнопки, позволяющие получить доступ к различным типам месторасположений:
26
− File System – Файловая система – позволяет просматривать дерево дисков и каталогов или совместно используемых ресурсов, предоставляемых другими компьютерами сети; − Local IIS – Локальный IIS – позволяет просматривать виртуальные каталоги, доступные с помощью программного обеспечения Webхостинга IIS, которое функционирует на текущем компьютере; − FTP Site (FTP-сайт) – менее удобно, так как перед подключением следует указать информацию о соединении, включая FTP-сайт, порт, каталог, имя пользователя и пароль; − Remote Site – Удаленный Web-сайт – предоставляет доступ к Webсайту по определенному URL с использованием HTTP. После возврата в диалоговое окно создания Web-сайта и указания всех необходимых опций, Visual Studio создаст новое Web-приложение. Новый Web-сайт начинается с одного файла – начальной страницы по умолчанию default.aspx, пример содержимого которого приведен на рис. 2.7.
Окно редактирования содержимого страницы
Переключение режимов редактирования страницы
Рис. 2.7. Созданное новое Web приложение.
Как видно из рисунка, основная часть файла представляет собой обычный структуру HTML документа с той лишь разницей, что в самом начале размещена строка, содержащая директивы ASP.NET, содержащая информацию об используемом языке программирования в модуле, связанном с данной страницей (в данном случае это C#), об автоматическом связывании событий данной страницы с функциями – обработчиками этих событий, а
27
также имя модуля, содержащего программный код, привязанный к данной странице. Наличие расширения .aspx у файла говорит о том, что он содержит некие директивы ASP.NET и должен быть исполнен платформой .NET Framework. При этом, очень важными элементами этого файла являются директивы среды исполнения, а также встроенные в страницу серверные элементы управления. Эти элементы должны обязательно располагаться внутри HTML элемента form, исполняющегося на стороне сервера и помечаются тегом . Например, описание серверного элемента Button, вставленного в страницу будет выглядеть следующим образом: .
Как видно из этого примера, после ключевого слова asp следует тип элемента, который соответствует его классу, описанному в .NET Framework, ID элемента, указание на его обработку на стороне сервера и другие параметры, такие, как подпись кнопки в данном случае. Особое значение при разработке приложений играет окно панели компонентов Toolbox. В Visual Studio окно Toolbox отображает элементы управления и компоненты, которые можно перетаскивать в окно документа. Содержимое Toolbox зависит от типа редактируемого документа. Например, при редактировании Web-формы Toolbox содержит серверные элементы управления, HTML-элементы управления, элементы управления, связанные с данными, и другие компоненты, которые можно размещать на поверхности Web-форм (Рис. 2.7). Файл Default.aspx.cs содержит программный код, привязанный к странице. Организация этого файла практически полностью повторяет организацию аналогичного файла для проекта Windows приложения. Пример содержимого только что созданного файла такого типа приведен на рис. 2.8. Как видно из рисунка, файл начинается с подключения различных пространств имен, содержащих описания тех классов .NET Framework, которые необходимо использовать в данном модуле. После чего следует описание класса страницы, состоящего из различных функций, в том числе привязанных к обработке событий данной формы. По умолчанию, создана заготовка функции – обработчика события открытия страницы Page_Load. Принцип разработки приложения в ASP.NET полностью соответствует объектно-ориентированному подходу. Программист в процессе создания Web приложения оперирует классами, определяя их атрибуты и их значения, а также методы, предназначенные для выполнения объектами класса действий, привязанных к событиям страницы. Компоненты в окне Toolbox (рис. 2.9) упорядочены по категориям. Щелкнув категорию, можно отобразить элементы из этой категории.
28
Содержимое окна Toolbox можно прокручивать, щелкая стрелки, расположенные сверху и снизу списка компонентов. Когда текущим документом является код, в окне Toolbox не отображаются компоненты, так как их невозможно размещать в рабочей области.
Раздел подключения пространств имен Класс страницы приложения
Заготовка функции Page_Load
Рис. 2.8. Элементы управления окна Toolbox в режиме редактирования исходного кода.
Редактирование Webдокументов Web-формы и HTML-документы можно редактировать в графическом режиме с использованием drag-and-drop, как обычные Windows-формы, либо в текстовом виде. Для переключения между режимами достаточно щелкнуть ярлычки Design и Source, расположенные внизу окна документа (рис. 2.7). Естественно, не все задачи можно решить в графическом режиме. Поэтому приходится редактировать Web-документы в режиме исходного кода. Если хорошо знать HTML, то этот способ может оказаться даже удобнее использования графических инструментов. Поддерживаемая Visual Studio технология IntelliSense помогает завершать элементы HTML, создаваемые в режиме исходного кода. Для демонстрации режимов работы со страницей рассмотрим пример создания простейшей страницы Web приложения, состоящего из кнопки, при нажатии на которую в окне Web браузера выводится текст «Это простейшее приложение ASP.NET». Для реализации данного примера воспользуемся созданным ранее пустым приложением. Поместим в файл Default.aspx кнопку. Сделать это можно двумя различными способами.
29
1. Перетащить кнопку из окна Toolbox в окно, содержащее код страницы. Для выполнения этой операции, необходимо отобразить в окне редактора файла .aspx исходный код файла, а затем перетащить из панели Toolbox в нужное место страницы кнопку. В результате, на странице будет размещен код кнопки, как показано на рис. 2.9.
Рис. 2.9. Пример размещения кнопки на странице с помощью перетаскивания в режиме редактирования исходного кода страницы.
2. Перетащить кнопку из окна Toolbox в окно, содержащее изображение дизайна страницы. Для выполнения этой операции необходимо отобразить в окне редактора файла .aspx дизайн страницы, а затем перетащить из панели Toolbox кнопку в любое место области окна страницы. Результат операции представлен на рис. 2.10.
Рис. 2.10. Пример размещения кнопки на странице с помощью перетаскивания в режиме редактирования дизайна страницы.
30
Переключение между режимами Design и Source возможно в любой момент в процессе редактирования содержимого страницы. Важным моментом при размещении элементов внутри Web страницы является то, что форматирование самой страницы подчиняется принципам форматирования HTML страниц. Это логично, т.к. костяк файла .aspx составляет именно HTML код страницы. Это означает в частности, что невозможно размещать элементы управления в произвольном месте Web страницы, так, как к этому привыкли разработчики Windows приложений. Для форматирования страниц Web приложений очень часто используются таблицы, позволяющие размещать элементы внутри ячеек, позиционируя таким образом элементы управления в нужном месте страницы. Подробнее о рекомендуемых принципах разработки пользовательского интерфейса Web приложений рассказывается в занятии 4. Следующим этапом нашего примера является создание обработчика события нажатия кнопки и вывода в окне браузера необходимого текста. Для создания обработчика события нажатия на кнопку необходимо переключиться в режим дизайна, выделить кнопку, открыть окно свойств выделенного объекта (окно Properties), это можно сделать с помощью щелчка правой кнопкой мыши на кнопке и выборе пункта Properties из открывшегося контекстного меню, переключиться в режим показа событий выбранного элемента управления (для этого необходимо нажать кнопку в окне свойств кнопки) и произвести двойной щелчок левой кнопкой мыши по событию, обработчик которого необходимо создать, в нашем примере это Click (Рис. 2.11). Кнопка выбора режима отображения событий текущего элемента управления
Выбор события для создания процедуры обработчика
Рис. 2.11. Окно свойств элемента управления Button.
В результате создается процедура Button1_Click, связанная с событием Click элемента управления Button. Эта процедура помещается в файл Default.aspx.cs. Введем в тело процедуры программный код, позволяющий вывести на экран текст. Для вывода в окно браузера информации возможно использовать класс Response, который применяется для формирования
31
отклика сервера, пересылаемого браузеру клиента. Более подробно объект Response рассматривается в занятии 4. Введем следующий код в обработчик события нажатия кнопки. Response.Write("Это простейшее приложение ASP.NET");
Окно редактора кода при этом будет выглядеть следующим образом (рис. 2.12).
Рис. 2.12. Окно редактора кода приложения после создания обработчика события нажатия на кнопку Button1.
Произведем запуск приложения. Для этого нажмем F5, либо произведем щелчок по кнопке панели инструментов Visual Studio 2005. Если Web приложение запускается впервые и для его запуска используется режим отладки (клавиша F5, либо вызов команды Debug Ö Start Debugging (F5), то отобразится диалог, изображенный на рис. 2.13.
Рис. 2.13. Диалоговое окно выбора режима запуска приложения при первом запуске.
32
Если выбрать режим Add a new Web.config file with debugging enabled, в файл web.config будет добавлена строка, включающая режим отладки запускаемого приложения. В дальнейшем, при повторном запуске данного приложения в режиме отладки это диалоговое окно отображаться не будет, а приложение будет сразу запускаться в режиме отладки. Если же выбрать режим Run without debugging, то приложение будет запущено в обычном режиме. После запуска проекта Visual Studio .NET компонует его файлы, показывая в окне Task List сообщения об ошибках. Дважды щелкнув описание ошибки в окне Task List, можно быстро найти строку с ошибкой, чтобы исправить ее. Если во время компоновки ошибки не обнаружены, Visual Studio запускает приложение в режиме отладки, а в случае Web-приложения – запускает установленный в системе браузер по умолчанию и открывает в нем начальную страницу Web-приложения. Если при исполнении приложения в режиме отладки возникает ошибка. Visual Studio .NET показывает в браузере соответствующее сообщение. Далее можно выполнить одно из следующих действий: • если причина ошибки известна, стоит остановить приложение, закрыв окно браузера, и вернуться в Visual Studio .NET, чтобы исправить ошибку; • если причина ошибки неизвестна, следует щелкнуть кнопку Back (Назад) в браузере и переключиться в Visual Studio .NET, чтобы установить в коде точку прерывания перед предполагаемым местом возникновения ошибки. Затем, переключившись обратно в браузер, попробовать еще раз выполнить действие, вызывавшее ошибку. Встретив точку прерывания, Visual Studio .NET остановит исполнение приложения. Далее путем пошагового исполнения возможно локализовать ошибку. После обнаружения причины ошибки, до ее устранения необходимо остановить приложение. После запуска приложения будет запущен локальный Web сервер, встроенный в Visual Studio. Уведомление об этом появится в панели индикации (рис. 2.14), после чего будет открыта страница Web приложения в браузере, установленном в системе по умолчанию.
Рис. 2.14. Уведомление о запуске локального Web сервера.
Страница созданного Web приложения, отображаемого в окне браузера, будет выглядеть следующим образом (рис. 2.15).
33
Рис. 2.15. Страница Web приложения, отображаемая в браузере Internet Explorer.
Как видно из рисунка, страница состоит из одной кнопки. Произведем щелчок левой кнопкой мыши по ней. В результате, на экране будет отображена строка «Это простейшее приложение ASP.NET» (рис.???)
Рис. 2.16. Результат отображения страницы после нажатия на кнопку Button.
Установка точек прерывания и просмотр значений переменных Можно остановить исполнение проекта перед некоторой строкой кода, установив в этой строке точку прерывания. Для этого следует щелкнуть на сером поле слева от строки, перед которой нужно остановить исполнение программы, либо, выделив эту строку, нажать F9. При этом строка выделяется, а слева от нее появляется точка, свидетельствующая о наличия точки прерывания программы (рис. 2.17).
34
точка прерывания
Рис. 2.17. Установленная точка прерывания.
Встретив при исполнении проекта строку с точкой прерывания, Visual Studio остановит приложение перед выполнением этой строки, выделит и откроет ее в окне редактора кода. После остановки выполнения приложения перед точкой прерывания стоит просмотреть значения активных переменных, наводя на них указатель мыши. Для просмотра значений переменных сложного типа, таких, как переменные-объекты или массивы, следует добавить эти переменные в окно Watch путем выбора и перетаскивания. Просмотреть значения вложенных элементов (например, элементов массива или свойств объекта) можно, щелкнив знак «+» справа от сложного элемента в окне Watch. Пошаговое исполнение Для возобновления исполнения приложения, остановленного перед точкой прерывания, следует щелкнуть кнопку Continue на панели инструментов либо нажать F5. Можно также исполнять приложение построчно, нажимая F10 (команда Debug Ö Step Over) или F11 (команда Debug Ö Step Into). Разница между ними заключается в следующем. F10 заставляет при вызове процедуры исполнить ее как одну команду. Другими словами, процедура исполняется без захода в нее (step over), после чего исполнение останавливается на следующей после вызова этой процедуры строке. Нажатие клавиши F11 вызывает исполнение процедуры с заходом в нее (step into), при этом исполнение останавливается на первой строке вызванной процедуры. Редактирование кода При создании новой Web-формы автоматически генерируется связанный с ней файл, содержащий объект кода этой страницы. Имя файла с
35
кодом составляется из имени Web-формы и расширения .vb или .cs (в зависимости от используемого языка программирования: cs соответствует языку C#, vb – Visual Basic). В каждом файле кода Visual Studio генерирует определение класса, инициализирующие процедуры и обработчик события Page_Load. Чтобы скрыть сгенерированный код, следует щелкнуть знак «минус» слева от директивы #Region. При этом область сворачивается, а знак «минус» меняется на «плюс», щелкнув который, область снова раскрывается. Аналогичным образом сворачивают и раскрывают и другие блоки кода, например определения классов и процедур (Рис. 2.17). Редактор кода также поддерживает функцию autocomplete, автоматически завершающую ключевые слова и члены классов, реализуемую при помощи технологии IntelliSense (Рис. 2.18). Окно подсказки появляется всякий раз, когда происходит ввод ключевого слова, представляющего собой команду, директиву, тип данных, либо зарезервированное слово. При этом пользователю достаточно выбрать из списка нужный вариант окончания данной команды и нажать клавишу Enter. В случае, если окно подсказки закрылось, а команда так и не была выбрана, пользователь его можно вызвать с помощью нажатия комбинации клавиш ctrl+пробел.
окно подсказки автоматического завершения набираемой команды
Рис. 2.18. Технология IntelliSense в действии при редактировании HTML-текста.
Если писать исходный код страницы на языке Visual Basic, то после завершения ввода каждой строки функция autocomplete будет делать первые буквы ключевых слов и имен членов заглавными. Однако в программе, написанной на Visual C# ключевые слова и члены классов не распознаются
36
если при их вводе использован неверный регистр, так как C#, в отличие от Visual Basic .NET, чувствителен к регистру символов. Кроме того, после ввода каждой строки редактор кода выделяет синтаксические ошибки и необъявленные переменные, подчеркивая их волнистой линией. При наведении указателя мыши на выделенную ошибку отображается ее описание. Большинство автоматизированных возможностей редактора кода можно включать и отключать, изменяя параметры в диалоговом окне Options. Кроме того, в нем предусмотрена корректировка автоматических абзацных отступов, завершения блоков кода и других параметров, определяющих работу с языком. Окно Solution Explorer. Структура ASP.NET приложения Visual Studio упорядочивает приложения при помощи проектов и решений. Проект (project) – это набор файлов, из которых в итоге компонуется исполняемый файл. Решение (solution) – это группа проектов, образующих функциональную единицу. Файлы, составляющие решение, можно просматривать в окне Solution Explorer, показанном на рис. 2.19. Стартовый проект (start-up project) запускается первым по щелчку кнопки Start в Visual Sludio. Если решение состоит из нескольких проектов, то стартовый проект обычно вызывает другие проекты этого решения. Сведения о решении хранятся в файле решения, который имеет расширение .sln и по умолчанию размещается в папке Мои документы. Эти файлы позволяют открывать решения, а файлы проектов Рис. 2.19. Окно Solution (файлы с расширением .vbproj или .csproj) позволяют Explorer. напрямую открывать проекты, расположенные в соответствующих папках. При сохранении проекта, открытого таким образом. Visual Studio создаст новый файл решения. Рассмотрим структуру созданного проекта, отображенную в окне Solution Explorer. После создания нового проекта, Visual Studio 2005 создает следующие файлы: Default.aspx и Default.aspx.cs (рис. 2.19). Кроме того, автоматически создается каталог App_Data, предназначенный для хранения файлов базы данных, используемой ASP.NET. Это может быть БД Access, SQL Server, XML или файл любого другого формата доступного в ASP.NET приложении. Помимо каталога App_Data в проекте Web приложения могут создаваться и другие каталоги. Часть из них играет предопределенное значение и занимают особое место в системе, их имена предопределены и за ними закреплены определенные функции. Такие каталоги часто называют виртуальными каталогами приложения. Другую часть составляют каталоги, создаваемые
37
самими пользователями. За такими каталогами не закреплены определенные функции, а их назначение в системе определяет сам пользователь. Более подробно виртуальные каталоги рассматриваются в занятии 4. Файл Default.aspx представляет собой файл, содержащий описание Web страницы в формате HTML. Таким образом, согласно такой модели организации проекта, ASP.NET придерживается принцип разделения исходного кода и интерфейса системы. Интерфейс системы описывается в файлах с расширением .aspx, а исходный код размещается в файлах с расширением .cs в случае, если в качестве языка программирования используется C# и .vb – если Visual Basic. Еще одним важным файлом проекта ASP.NET приложения является файл Web.Config. При начальном создании проекта, Visual Studio не создает этот файл. Однако, при первом запуске приложения (с помощью нажатия клавиши F5), пользователю предлагается создать этот файл и установить в нем опцию, разрешающую отладку данного приложения, как было показано выше (рис. 2.13). Файл Web.Config является конфигурационным файлом, созданном на базе формата XML. Фрагмент такого файла, изображен на рис. 2.20. Он содержит большое количество параметров настройки на уровне приложения, которые конфигурируют все аспекты, начиная с безопасности, и заканчивая отладкой, подключением к источникам данных и управлением состояния.
Рис. 2.20. Пример файла Web.Config.
38
Однако, кроме перечисленных выше файлов, приложение ASP.NET может содержать и другие элементы, играющие свою роль и выполняющие свои задачи. Все эти элементы могут быть добавлены в проект по ходу его развития. Для более глубокого понимания принципов организации и создания Web приложения на основе ASP.NET, рассмотрим некоторые из них более подробно. Файлы .asmx – представляют собой Web службы обычно реализуются в виде специальным образом оформленных процедур или функций, запускающихся на стороне сервера и способных обмениваться данными с любым приложением, функционирующим на любой платформе и поддерживающего SOAP. Файл Global.asax – содержит обработчики событий, которые реагируют на глобальные события приложения. Структура и содержимое этого файла более подробно рассмотрены в [1]. Возможны и другие компоненты, к которым относятся скомпилированные сборки, созданные сторонними разработчиками или самим программистом, позволяющие разделять бизнес логику и логику доступа к данным и создавать специальные элементы управления. Для того, чтобы узнать какие еще компоненты могут быть добавлены к проекту Web приложения на базе ASP.NET, необходимо выполнить следующую команду Website Ö Add New Item, либо нажать Ctrl+Shift+A. В открывшемся окне представлены все элементы, которые возможно добавить к проекту (рис. 2.21).
Рис. 2.21. Добавление компонентов в проект.
Мы будем рассматривать возможности добавляемых к проекту компонентов по мере необходимости.
39
Работа со справочной системой В Visual Studio имеется объединенная справочная система, включающая сведения по среде разработки, языкам программирования и .NET Framework, а также информацию технической поддержки и статьи из MSDN. В зависимости от параметров, установленных на начальной странице в диалоговом окне Options, содержимое справочной системы отображается в окне документа либо в отдельном окне вне среды Visual Studio. В справочной системе предусмотрены три поисковых окна: Contents, Index и Search. Они работают подобно инструментальным окнам Visual Studio: их можно «пристыковывать» к другим окнам, а также прятать и открывать, щелкая их ярлычки (Рис. 2.22).
поисковое окно Search строка поискового запроса
строка быстрого поиска по ключевому слову
окно результатов обработки поискового запроса
Рис. 2.22. Окно справочной системы Visual Studio 2005.
В каждом поисковом окне есть раскрывающийся список Filtered by, в котором выбирают нужный язык программирования или раздел для поиска. Эта возможность особенно полезна при работе с окнами Search и Index, поскольку объединенная справочная система содержит немало информации. В разделах с примерами операторов и кода на каждой странице установлен фильтр языка. Он имеет вид кнопки с изображением воронки и расположен в левом верхнем углу страницы. Помимо материалов справочной системы в Visual Studio доступны также ресурсы целого ряда интернет сайтов, поддерживаемых Microsoft и ее партнерами, на которых размещено и постоянно публикуется большое
40
количество интересной информации для разработчиков на платформе .Net Framework. Среди русскоязычных сайтов, посвященных данной тематике, можно выделить GotDotNet.ru, адрес в интернете www.gotdotnet.ru и www.aspnetmania.com Запуск приложения и организация виртуальных каталогов приложений Как известно, для функционирования ASP.NET приложения необходим Web сервер, способный взаимодействовать с данной средой исполнения. Размещение Web приложений на сервере обычно происходит в виртуальных каталогах, представляющих собой обычные каталоги, доступные через Web сервер. Виртуальные каталоги играют большое значение, так как влияют на выполнение ASP.NET программного кода. Так, в процессе исполнения Web приложения, Web страницы и Web службы, находящиеся в одном виртуальном каталоге выполняются в рамках одного домена приложения. В случае же, если они расположены в разных виртуальных каталогах, их выполнение происходит в разных доменах. Домен приложения представляет собой эквивалент процесса, реализованный в .NET. Он гарантирует невозможность одного приложения оказывать влияние на другое, то есть использовать пересекающиеся области памяти. Для создания доменов, ASP.NET использует механизм отложенной инициализации, при котором домен приложения создается в момент получения первого запроса на страницу или Web службу в этом приложении. Домен приложения может быть закрыт в силу различных причин, включая произошедшую в приложении ошибку. ASP.NET способен повторно использовать домены приложений. Это означает, что в случае возникновения ошибки, произойдет перезапуск домена в момент очередного обращения пользователя к Web странице или службе. Кроме того, при изменении приложения также происходит создание нового домена, который будет обслуживать все вновь поступающие запросы к приложению. Старый домен, при этом, будет сохранен до тех пор, пока не будут обработаны все оставшиеся запросы (включая запросы, находящиеся в очереди). Следствием этой особенности является то, что обновление Web приложения возможно производить в любой момент не перезапуская Web сервер и не беспокоясь о причинении вреда клиентам, работающим в данный момент с ним. Структура каталога приложения Виртуальный каталог может содержать большое количество ресурсов, используемых Web приложением. К ним относятся таблицы стилей, изображения, XML файлы и т.д. В тоже время, в виртуальном каталоге могут содержаться подкаталоги, которые, в зависимости от своего названия, могут
41
выполнять специальные функции. Ниже перечислены специальные каталоги ASP.NET. Bin
содержит все предварительно скомпилированные сборки .NET, которые обычно представляют собой DLL библиотеки. Эти библиотеки используются Web приложением и могут включать предварительно скомпилированные классы Web страниц и служб, а также другие сборки, на которые ссылаются данные классы. содержит классы исходного кода, динамически скомпилированные для использования в рамках приложения. Обычно эти файлы кода представляют собой отдельные компоненты, такие как библиотеки доступа к данным, Web сервисы и т.п. хранит глобальные ресурсы, доступные каждой странице Web приложения. хранит локальные ресурсы, доступные только специальной странице. хранит ссылки на Web службы, используемые приложением. Хранит файлы данных, включая XML файлы и файлы SQL Express Содержит определения браузера, записанные в формате XML. Эти файлы определяют характеристики браузеров на стороне клиентов и влияют на визуализацию страницы. хранит темы, используемые Web приложением.
Для упрощения отладки рассматриваемых примеров, рекомендуем воспользоваться возможностями встроенного в Visual Studio Web сервера, обеспечивающего туже функциональность, что и IIS, за исключением того, что к нему возможны подключения только с локальной машины (той, на которой расположено само Web приложение).
42
Занятие 3.Основы языка C# Основные операторы языка C# Состав и синтаксис операторов C# унаследован от языка С++, тем не менее, существует ряд отличий, которые улучшают некоторые характеристики C++ делая его более легким в использовании. Предполагая, что читатель уже знаком с основными операторами языка C++ и имеет минимальный опыт программирования, остановимся на рассмотрении только наиболее значимых операторов C#, а также операторов, специфических для данного языка программирования. Цикл foreach Новый вид цикла, который появился в C# и отсутствует в C++ является цикл foreach. Он удобен при работе с массивами, коллекциями и другими контейнерами данных. Синтаксис оператора выглядит следующим образом. foreach (тип идентификатор in контейнер) оператор
Тело цикла выполняется для каждого идентификатора в контейнере. Тип идентификатора должен быть согласован с типом элементов, хранящихся в контейнере. На каждом шаге цикла идентификатор, задающий текущий элемент контейнера, получает значение очередного элемента в соответствии с порядком, установленным на элементах контейнера. С использованием этого текущего элемента и выполняется тело цикла. Количество шагов цикла равно количеству элементов, находящихся в контейнере. Таким образом, цикла заканчивается в тот момент, когда были перебраны все элементы контейнера. Важной особенностью этого цикла является то, что в теле цикла элементы контейнера доступны только для чтения. Поэтому заполнять коллекции с использованием цикла foreach нельзя, необходимо пользоваться другими видами циклов. В следующем примере демонстрируется работа с двухмерным массивом, который вначале заполняется случайными числами с помощью цикла for, а затем, с помощью цикла foreach подсчитывается сумма всех элементов массива, а также находятся минимальный и максимальный элементы. public void SumMinMax() { int[,] myArray = new int[10, 10]; Random rnd = new Random(); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { myArray[i, j] = rnd.Next(100); Response.Write(myArray[i, j] + " }
");
43 Response.Write(" "); } long sum = 0; int min = myArray[0, 0]; int max = myArray[0, 0]; foreach (int i in myArray) { sum += i; if (i > max) max = i; if (i < min) min = i; } Response.Write("Sum=" + sum.ToString() + " Min=" + min.ToString() + " Max=" + max.ToString()); }
Типы данных. Преобразования типов. Типы данных принято разделять на простые и сложные в зависимости от того, как устроены их данные. У простых (скалярных) типов возможные значения данных едины и неделимы. Сложные типы характеризуются способом структуризации данных - одно значение сложного типа состоит из множества значений данных, организующих сложный тип. Типы данных разделяются также на статические и динамические. Для данных статического типа память отводится в момент объявления, требуемый размер данных (памяти) известен при их объявлении. Для данных динамического типа размер данных в момент объявления обычно неизвестен и память им выделяется динамически по запросу в процессе выполнения программы. Еще одна важная классификация типов - это их деление на значимые и ссылочные. Для значимых типов значение переменной (объекта) является неотъемлемой собственностью переменной (точнее, собственностью является память, отводимая значению, а само значение может изменяться). Для ссылочных типов значением служит ссылка на некоторый объект в памяти, расположенный обычно в динамической памяти – «куче». Объект, на который указывает ссылка, может быть разделяемым. Это означает, что несколько ссылочных переменных могут указывать на один и тот же объект и разделять его значения. Значимый тип принято называть развернутым, подчеркивая тем самым, что значение объекта развернуто непосредственно в памяти, отводимой объекту. Для большинства процедурных языков, реально используемых программистами - Паскаль, C++, Java, Visual Basic, C#, - система встроенных типов более или менее одинакова. Всегда в языке присутствуют арифметический, логический (булев), символьный типы. Арифметический тип всегда разбивается на подтипы. Всегда допускается организация данных в виде массивов и записей (структур). Внутри арифметического типа всегда допускаются преобразования, всегда есть функции, преобразующие строку в число и обратно. Поскольку язык C# является непосредственным потомком языка C++, то и системы типов этих двух языков близки и совпадают вплоть до
44
названия типов и областей их определения. Но отличия, в том числе принципиального характера, есть и здесь. Система типов Давайте рассмотрим, как устроена система типов в языке C#, но вначале для сравнения приведем классификацию типов в стандарте языка C++. Стандарт языка C++ включает следующий набор фундаментальных типов. 1. Логический тип – bool. 2. Символьный тип – char. 3. Целые типы. Целые типы могут быть одного из трех размеров short, int, long, сопровождаемые описателем signed или unsigned, который указывает, как интерпретируется значение, - со знаком или без оного. 4. Типы с плавающей точкой. Эти типы также могут быть одного из трех размеров -float, double, long double. 5. Тип void, используемый для указания на отсутствие информации. 6. Указатели (например, int* - типизированный указатель на переменную типа int). 7. Ссылки (например, double& - типизированная ссылка на переменную типа double). 8. Массивы (например, char[] - массив элементов типа char). 9. Перечислимые типы enum для представления значений из конкретного множества. 10. Структуры – struct. 11. Классы. Первые три вида типов называются интегральными или счетными. Значения их перечислимы и упорядочены. Целые типы и типы с плавающей точкой относятся к арифметическому типу. Типы подразделяются также на встроенные и типы, определенные пользователем. Эта схема типов сохранена и в языке C#. Однако здесь на верхнем уровне используется и другая классификация, носящая для C# принципиальный характер. Согласно этой классификации все типы можно разделить на четыре категории: 1. Типы-значения – value, или значимые типы. 2. Ссылочные – reference. 3. Указатели – pointer. 4. Тип void. Эта классификация основана на том, где и как хранятся значения типов. Для ссылочного типа значение задает ссылку на область памяти в «куче», где расположен соответствующий объект. Для значимого типа используется прямая адресация, значение хранит собственно данные, и память для них отводится, как правило, в стеке.
45
В отдельную категорию выделены указатели, что подчеркивает их особую роль в языке. Указатели имеют ограниченную область действия и могут использоваться только в небезопасных блоках, помеченных как unsafe. Особый статус имеет и тип void, указывающий на отсутствие какоголибо значения. В языке C# жестко определено, какие типы относятся к ссылочным, а какие к значимым. К значимым типам относятся: логический, арифметический, структуры, перечисление. Массивы, строки и классы относятся к ссылочным типам. На первый взгляд, такая классификация может вызывать некоторое недоумение, почему это структуры, которые в C++ близки к классам, относятся к значимым типам, а массивы и строки - к ссылочным. Однако ничего удивительного здесь нет. В C# массивы рассматриваются как динамические, их размер может определяться на этапе вычислений, а не в момент трансляции. Строки в C# также рассматриваются как динамические переменные, длина которых может изменяться. Поэтому строки и массивы относятся к ссылочным типам, требующим распределения памяти в «куче». Со структурами дело обстоит иначе. Структуры C# представляют частный случай класса. Определив свой класс как структуру, программист получает возможность отнести класс к значимым типам, что иногда бывает крайне полезно. В C# только благодаря структурам появляется возможность управлять отнесением класса к значимым или ссылочным типам. Правда, это неполноценное средство, поскольку на структуры накладываются дополнительные ограничения по сравнению с обычными классами. Согласно принятой классификации все типы делятся на встроенные и определенные пользователем. Все встроенные типы C# однозначно отображаются, а фактически совпадают с системными типами каркаса .Net Framework, размещенными в пространстве имен System. Поэтому всюду, где можно использовать имя, например, int, с тем же успехом можно использовать и имя System.Int32. Система встроенных типов языка C# не только содержит практически все встроенные типы (за исключением long double) стандарта языка C++, но и перекрывает его разумным образом. В частности тип string является встроенным в язык, что вполне естественно. В области совпадения сохранены имена типов, принятые в C++, что облегчает жизнь тем, кто привык работать на C++, но собирается по тем или иным причинам перейти на язык C#. Язык C# в большей степени, чем язык C++, является языком объектного программирования. В языке C# сглажено различие между типом и классом. Все типы - встроенные и пользовательские - одновременно являются классами, связанными отношением наследования. Родительским, базовым классом является класс Object. Все остальные типы или, точнее, классы являются его потомками, наследуя методы этого класса. У класса Object есть четыре наследуемых метода:
46
1. bool Equals (object obj) – проверяет эквивалентность текущего объекта и объекта, переданного в качестве аргумента; 2. System.Type GetType () – возвращает системный тип текущего объекта; 3. string ToString () – возвращает строку, связанную с объектом. Для арифметических типов возвращается значение, преобразованное в строку; 4. int GetHashCode() – служит как хэш-функция в соответствующих алгоритмах поиска по ключу при хранении данных в хэш-таблицах. Естественно, что все встроенные типы нужным образом переопределяют методы родителя и добавляют собственные методы и свойства. Учитывая, что и типы, создаваемые пользователем, также являются потомками класса Object, то для них необходимо переопределить методы родителя, если предполагается использование этих методов; реализация родителя, предоставляемая по умолчанию, не обеспечивает нужного эффекта. Рассмотрим несколько примеров. Начнем с вполне корректного в языке C# примера объявления переменных и присваивания им значений: int x=11; int v = new Int32(); v = 007; string s1 = "Agent"; s1 = s1 + v.ToString() +x.ToString();
В этом примере переменная x объявляется как обычная переменная типа int. В то же время для объявления переменной v того же типа int используется стиль, принятый для объектов. В объявлении применяется конструкция new и вызов конструктора класса. В операторе присваивания, записанном в последней строке фрагмента, для обеих переменных вызывается метод ToString, как это делается при работе с объектами. Этот метод, наследуемый от родительского класса Object, переопределенный в классе int, возвращает строку с записью целого. Сообщу еще, что класс int не только наследует методы родителя - класса Object, - но и дополнительно определяет метод CompareTo, выполняющий сравнение целых, и метод GetTypeCode, возвращающий системный код типа. Для класса Int определены также статические методы и поля, о которых расскажу чуть позже. Так что же такое после этого int, спросите Вы: тип или класс? Ведь ранее говорилось, что int относится к value-типам, следовательно, он хранит в стеке значения своих переменных, в то время как объекты должны задаваться ссылками. С другой стороны, создание экземпляра с помощью конструктора, вызов методов, наконец, существование родительского класса Object, - все это указывает на то, что int - это настоящий класс. Правильный ответ состоит в том, что int - это и тип, и класс. В зависимости от контекста x может восприниматься как переменная типа int или как объект класса int. Это же верно и для всех остальных значимых типов. Стоит отметить, что все значимые типы фактически реализованы как структуры, представляющие частный случай класса.
47
Такая двойственность в языке C# обусловлена тем, что значимые типы эффективнее в реализации, им проще отводить память, так что именно соображения эффективности реализации заставили авторов языка сохранить значимые типы. Более важно, что зачастую необходимо оперировать значениями, а не ссылками на них, хотя бы из-за различий в семантике присваивания для переменных ссылочных и значимых типов. С другой стороны, в определенном контексте крайне полезно рассматривать переменные типа int как настоящие объекты и обращаться с ними как с объектами. В частности, полезно иметь возможность создавать и работать со списками, чьи элементы являются разнородными объектами, в том числе и принадлежащими к значимым типам. Семантика присваивания Рассмотрим присваивание: x = e. Чтобы присваивание было допустимым, типы переменной x и выражения e должны быть согласованными. Пусть сущность x согласно объявлению принадлежит классу T. Будем говорить, что тип T основан на классе T и является базовым типом x, так что базовый тип определяется классом объявления. Пусть теперь в рассматриваемом нами присваивании выражение e связано с объектом типа T1. Определение: тип T1 согласован по присваиванию с базовым типом T переменной x, если класс T1 является потомком класса T. Присваивание допустимо, если и только если имеет место согласование типов. Так как все классы в языке C# - встроенные и определенные пользователем - по определению являются потомками класса Object, то отсюда и следует наш частный случай - переменным класса Object можно присваивать выражения любого типа. Несмотря на то, что обстоятельный разговор о наследовании, родителях и потомках нам еще предстоит, лучше с самого начала понимать отношения между родительским классом и классом-потомком, отношения между объектами этих классов. Класс-потомок при создании наследует все свойства и методы родителя. Родительский класс не имеет возможности наследовать свойства и методы, создаваемые его потомками. Наследование - это односторонняя операция от родителя к потомку. Ситуация с присваиванием симметричная. Объекту родительского класса присваивается объект класса-потомка. Объекту класса-потомка не может быть присвоен объект родительского класса. Присваивание - это односторонняя операция от потомка к родителю. Одностороннее присваивание реально означает, что ссылочная переменная родительского класса может быть связана с любыми объектами, имеющими тип потомков родительского класса. Например, пусть задан некоторый класс Parent, а класс Child - его потомок, объявленный следующим образом: class Child:Parent {...}
48
Пусть теперь в некотором классе, являющемся клиентом классов Parent и Child, объявлены переменные этих классов и созданы связанные с ними объекты: Parent p1 = new Parent(), p2 = new Parent(); Child ch1 = new Child(), ch2 = new Child();
Тогда допустимы присваивания:
p1 = p2; p2= p1; ch1=ch2; ch2 = ch1;
p1 = ch1; p1 = ch2;
Но недопустимы присваивания: ch1 = p1; ch2 = p1; ch2 = p2; Заметьте, ситуация не столь удручающая - сын может вернуть себе переданный родителю объект, задав явное преобразование. Так что следующие присваивания допустимы: p1 = ch1; ... ch1 = (Child)p1;
Семантика присваивания справедлива и для другого важного случая при рассмотрении соответствия между формальными и фактическими аргументами процедур и функций. Если формальный аргумент согласно объявлению имеет тип T, а выражение, задающее фактический аргумент, имеет тип T1, то имеет место согласование типов формального и фактического аргумента, если и только если класс T1 является потомком класса T. Отсюда незамедлительно следует, что если формальный параметр процедуры принадлежит классу Object, то фактический аргумент может быть выражением любого типа. Преобразование к типу object
Рассмотрим частный случай присваивания x = e; когда x имеет тип object. В этом случае гарантируется полная согласованность по присваиванию – выражение e может иметь любой тип. В результате присваивания значением переменной x становится ссылка на объект, заданный выражением e. Заметьте, текущим типом x становится тип объекта, заданного выражением e. Уже здесь проявляется одно из важных различий между классом и типом. Переменная, лучше сказать сущность x, согласно объявлению принадлежит классу Object, но ее тип - тип того объекта, с которым она связана в текущий момент, - может динамически изменяться. Массивы, перечисления, коллекции Массивы Массивы в C# с точки зрения синтаксиса практически ни чем не отличаются от массивов в языке C++ и Java. Тем не менее, внутреннее устройство массивов C# сильно отличается от упомянутых языков. Дело в том, что любой массив в C# является потомком класса System.Array со всеми вытекающими последствиями, связанными с наследованием каждым
49
массивов свойств и методов класса Array. Более подробно класс Array рассматривается ниже. Формально массив можно определить как набор однотипных элементов, доступ к которым производится с помощью числового индекса. Элементами массива могут быть значения произвольного типа, в том числе массивы, классы, структуры, интерфейсы. Массивы могут быть как одномерными, так и многомерными. Объявление массива происходит путем помещения квадратных скобок после указания типа данных для элемента массива. Например. Создание массива d, состоящего из десяти элементов типа int. При этом необходимо учитывать тот факт, что нумерация элементов начинается с нуля. Т.е. в данном случае массив будет состоять из десяти элементов с номерами {0…9}. int[] d; d = new int[10];
Аналогичное предыдущему действие, требующее всего одной строки кода. int[] d = new int[10];
Еще один пример создания массива, состоящего из двенадцати элементов типа string. string[] st = new string[12];
При создании массивов необходимо учитывать, что для создания массива фиксированной длины, как в предыдущих примерах, необходимо всегда использовать ключевое слово new. Таким образом, следующее определение массива недопустимо и приведет к возникновению ошибки еще на этапе компиляции. int[3] a={3,5,6};
Несмотря на это, если мы явно не указываем размер массива при его создании, а возлагаем эти обязанности на копмпилятор, ключевое слово new можно не использовать. В связи с этим следующее определение массива допустимо. int[] a={3,5,6};
Заполнение массивов возможно осуществлять как и в C++ с использованием фигурных скобок, как уже было показано выше, либо обращаясь к элементам массива по номерам элементов. int[] a=new int[3]{3,5,6};
50 int[] a = new int[3]; a[0] = 3; a[1] = 5; a[2] = 6;
Важным отличием массивов C# от C++ является то, что при создании массива в C# его элементам автоматически присваиваются значения по умолчанию, в отличие от C++, где значения элементов только что созданного массива оказываются не определены. Присваиваемые значения по умолчанию зависят от типа элемента. Так, для объектов значением по умолчанию является NULL, а для целых чисел – 0. Помимо простых одномерных массивов C# поддерживает также многомерные массивы, которые в свою очередь делятся на две категории. К первой категории относятся массивы, количество элементов каждой строки которых состоит из одинакового количества элементов. Таким образом, массив можно рассматривать как прямоугольник, а сами такие массивы называют «прямоугольными». Ко второй категории относятся массивы, количество элементов в строках у которых не одинаково. Такие массивы образуют прямоугольник, у которого одна сторона представляет ломаную линию, поэтому такие массивы называют «ломаными». Рассмотрим простейшие манипуляции для работы с прямоугольными многомерными массивами. Объявление и заполнение массива производится стандартным способом: для этого организуется два цикла. Random rnd=new Random(); int[,] Matrix; Matrix = new int[5, 4]; for (int i = 0; i < 5; i++) for (int j=0;j<4;j++) Matrix[i,j] = rnd.Next(10,99);
При необходимости вывода значений элементов массива на экран, также организуется цикл, позволяющий последовательно перебирать все элементы массива. for (int i = 0; i < 5; i++) { for (int j = 0; j < 4; j++) { Response.Write(Matrix[i, j].ToString()); Response.Write(" "); } Response.Write(" "); }
Результат работы программы показан на рис. 3.1.
51
Рис. 3.1. Результат работы программы с «прямоугольным» массивом.
«Ломаный» массив может рассматриваться как массив, каждая ячейка которого представляет собой массив. В качестве примера создадим массив, с переменным количеством элементов в строках. Random rnd = new Random(); int[][] JMatrix = new int[5][]; for (int i = 0; i < JMatrix.Length; i++) { JMatrix[i] = new int[rnd.Next(1,7)]; }
Количество элементов в строке определяется случайным образом в момент формирования массива. Количество строк задано жестко и равно пяти. Массив не заполняется никакими числами, поэтому на экран будут выводиться значения, которыми заполняется массив по умолчанию (в данном случае это ноль). Для вывода информации на экран, необходимо использовать два цикла как это показано ниже. for (int i = 0; i < JMatrix.Length; i++) { Response.Write("Количество элементов в строке " + i.ToString() + "=" + JMatrix[i].Length.ToString()+" "); for (int j = 0; j < JMatrix[i].Length; j++) { Response.Write(JMatrix[i][j].ToString() + " "); } Response.Write(" "); }
В цикле по j происходит определение количества элементов в строке с помощью свойства Length. Результат работы программы представлен на рис. 3.2.
52
Рис. 3.2. Результат вывода на экран содержимого «ломаного» массива.
Класс Array Для более детального понимания особенностей использования массивов в C# необходимо рассмотреть специализированный класс, реализующий функции массива. Как уже упоминалось ранее, все типы данных в C# являются классами, в качестве базового для которых выступает класс Object. Класс Array не исключение. Он реализует все базовые свойства классов и является предком для всех типов массивов, к которым мы привыкли в языке C++ и синтаксис описания которых был приведен выше. То, что класс Array является потомком класса Object является, на первый взгляд, малоинтересным фактом, однако, на самом деле, говорит об очень многом. В частности, это приводит к тому, что в классе Array оказывается определено множество разнообразных операций, таких как копирование, поиск, обращение, сортировки и т.д. Ниже приведены наиболее интересные методы класса Array. BinarySearch() Sort() Clear() CopyTo() GetLength(), Length GetLowerBound() GetUpperBound() GetValue() SetValue() Reverse() Rank
Поиск элементов в одномерном отсортированном массиве. Сортировка элементов одномерного массива. Очистка элементов массива в заданном диапазоне индексов. Копирование элементов исходного массива в массив назначения. Определение количества элементов в указанном измерении массива. Определение нижней границы массива. Определение верхней границы массива. Возвращает значение указанного индекса для массива. Устанавливает значение указанного индекса для массива. Расставляет элементы одномерного массива в обратном порядке. Определение количества измерений указанного массива.
Рассмотрим пример использования класса Array. Для этого создадим массив и обеспечим возможность поиска в нем элементов.
53
Объявим массив myArray как статический член класса из шести элементов типа int.
Разместим на форме элементы TextBox и Button, которым присвоим имена tb_value и btn_find соответственно, как показано на рис. 3.3.
Рис. 3.3. Размещение на форме элементов TextBox и Button.
В обработчик процедуры нажатия на кнопку введем следующий код, заполняющий массив случайными числами, сортирующий его и осуществляющий поиск введенного в текстовое поле элемента. Код процедуры нажатия на кнопку приведен ниже. protected void btn_find_Click(object sender, EventArgs e) { Random rnd = new Random(); Response.Write("Исходный массив "); for (int i = myArray.GetLowerBound(0); i <= myArray.GetUpperBound(0); i++) { myArray.SetValue(rnd.Next(1, 10), i); Response.Write(myArray.GetValue(i) + "\t"); } Response.Write(" "); Array.Sort(myArray); Response.Write("После сортировки: "); for (int i = myArray.GetLowerBound(0); i <= myArray.GetUpperBound(0); i++) { Response.Write(myArray.GetValue(i) + "\t"); } object o = Convert.ToInt32(tb_value.Text); int findIndex = Array.BinarySearch(myArray, o); if (findIndex<0) { Response.Write(" Элемент не найден"); } else { Response.Write(" Элемент найден в позиции "+findIndex.ToString()+""); } }
Пример работы программы в результате выполнения приведенного выше кода представлен на рис. 3.4.
54
Рис. 3.4. Результат работы программы заполнения, сортировки и поиска элементов массива.
Как видно, работать с массивами в C# достаточно просто в особенности учитывая довольно большие возможности класса Array. Перечисления При работе с современными языками программирования, можно заметить, что часто вместо цифровых констант и значений используются имена. Так происходит, например, при указании цвета того или иного элемента управления, либо объекта. Обычно цвет задается в виде кодов, соответствующих интенсивности каждой его составляющей: красного, зеленого, синего цвета, однако, это не удобно, т.к. не является наглядным представлением получаемого в результате цвета. Для решения задачи упрощения использования цветов применяется именованная совокупность цветов, сопоставляющая код с наименованием. Это дает возможность в программном коде использовать имена наиболее часто употребляемых цветов. Например, цвет текста, выводимого на экран с помощью элемента Label можно задать с помощью команды Label1.ForeColor=Color.Red;
В данном случае, имя Red соответствует красному цвету из набора цветов Colors. Такие наборы называются перечислениями. Перечисление можно создать следующим образом: enum OrganizationType { Education, Trade, Manufacture, Services }
Перечисление OrganizationType определяет четыре именованные константы, каждой из которых соответствует определенное числовое
55
значение. По умолчанию, первому элементу перечисления соответствует значение 0, второму 1, третьему 2 и т.д. При необходимости точку отсчета номеров можно изменить. В следующем примере нумерация элементов перечисления начинается с числа 10. enum OrganizationType { Education=10, Trade, Manufacture, Services }
Номера, присваиваемые переменным не обязательно должны идти последовательно. Они могут быть расположены в произвольной последовательности. По умолчанию, к элементам перечисления применяется тип данных int. Однако для элементов перечисления можно использовать любой целочисленный тип данных, например byte. enum OrganizationType:byte { Education, Trade, Manufacture, Services }
Все перечисления в C# происходят от единого базового класса System.Enum. Этот класс содержит методы, существенно облегчающие работу с перечислениями. Так, статический метод GetUnderlyingType() позволяет получить информацию о том, какой тип данных используется для представления числовых значений элементов перечисления. Например, следующая строка выведет на экран System.Byte, соответствующий типу элементов перечисления OrganizationType. Response.Write(Enum.GetUnderlyingType(typeof(OrganizationType)));
позволяет получить значимые имена элементов перечисления по их числовым значениям. Для этого используется статический метод Format(). Следующий пример выведет на экран наименование элемента перечисления OrganizationType, которому соответствует значение 2 (в данном случае Manufacture). Enum
Параметр «G», указанный в функции Format задает режим вывода строки результата. G означает, что результат будет выводиться в виде строки.
56
Возможны еще два параметра форматирования. X – шестнадцатеричное представление результата, d – десятичное значение. В классе System.Enum существует также метод, позволяющий создать массив на основе указанного перечисления. Например, следующая строка создает массив a, состоящий из 4 элементов. В первый элемент будет записано значение Education, во второе – Trade и т.д. Array a = Enum.GetValues(typeof(OrganizationType));
При необходимости определения того факта, является ли символьная строка элементом перечисления, необходимо использовать свойство IsDefined класса Enum. Следующие примеры демонстрируют применение этого свойства. В первом случае результат проверки будет положительным, во втором – отрицательным. bool r = Enum.IsDefined(typeof(OrganizationType), "Trade"); bool r1 = Enum.IsDefined(typeof(OrganizationType), "Entertainment");
Работа со строками в C# По аналогии с массивами, все строки в C# происходят от одного базового класса – System.String, в котором реализовано достаточно много различных методов, осуществляющий всевозможные операции над строками. Наиболее интересные методы класса String представлены ниже. Length Concat()
Позволяет получить количество символов в строке. Позволяет соединить несколько строк или переменных типа object. Позволяет сравнить две строки. В случае равенства строк результат выполнения функции равен нулю. При положительном значении функции большей является строка, для которой вызывался метод. Создает новую копию существующей строки Используется для форматирования строки с использованием различных примитивов (строк и числовых данных) и подстановочных выражений вида {0}. Позволяет вставить одну строку внутрь существующей. Удаляют или заменяют символы в строке Преобразуют все символы строки в строчные или прописные. Позволяет получить символ, находящийся в определенной позиции строки. Создает строку, соединяя заданные строки и разделяя их строкой разделителем. Заменяет один символ строки другим.
57 Split()
Substring()
Trim() ToCharArray()
Возвращает массив строк, элементами которого являются подстроки основной строки, между которыми находятся символы-разделители. Позволяет получить подстроку основной строки, начинающуюся с определенного символа и имеющую заданную длину. Удаляет пробелы, либо набор заданных символов в начале и конце основной строки. Создает массив символов и помещает в него символы исходной строки.
При работе со строками в C# необходимо учитывать следующее. Тип string является ссылочным типом. Однако, несмотря на это, при использовании операций сравнения происходит сравнение значений строковых объектов, а не адресов этих объектов, размещенных в оперативной памяти. Кроме того, оператор «+» объекта string перегружен так, что при его использовании используется метод Concat(). Рассмотрим несколько примеров использования возможностей работы со строками. В следующем примере функция MakeLine создает строку, состоящую из символов, получающихся путем несложных вычислений. Результаты вычислений заносятся в массив строк. Таким образом, массив sArr после завершения цикла содержит значения всех полученных чисел. После этого, используя разделить, передаваемый в функцию в качестве аргумента, а также функцию Join значения всех ячеек массива sArr объединяются в строку. protected void Page_Load(object sender, EventArgs e) { Response.Write(MakeLine(0, 5, ", ")); Response.Write(""); Response.Write(MakeLine(1, 6, " ")); Response.Write(""); Response.Write(MakeLine(9, 9, ": ")); Response.Write(""); Response.Write(MakeLine(4, 7, "< ")); } private static string MakeLine(int initVal, int multVal, string sep) { string[] sArr = new string[10]; for (int i = initVal; i < initVal+10; i++) sArr[i-initVal] = String.Format("{0,-3}", i * multVal); }
return String.Join(sep, sArr);
Результат работы программы представлен на рис. 3.5.
58
Рис. 3.5. Результат работы программы с использованием функции Join.
Довольно часто при работе со строками возникает необходимость разделить строку на подстроки, отделенные друг от друга заданными символами-разделителями. В следующем примере задается строка символов, в которой присутствует несколько символов-разделителей. При помощи функции Split данная строка разделяется на подстроки, которые затем выводятся на экран каждая в отдельной строке. Для задания символовразделителей используется массив символов. В данном примере также используется функция Trim, необходимая, в данном случае, для того, чтобы убедиться в том, что заданная строка не состоит из одних лишь пробелов. Исходный код данного примера приведен ниже. string words = "строка, содержащая несколько слов, а также знаков пунктуации: таких как двоеточие и точка."; string [] split = words.Split(new Char [] {' ', ',', '.', ':'}); foreach (string s in split) { if (s.Trim() != "") Response.Write(s+""); }
В результате работы данной программы будет получен следующий результат, изображенный на рис. 3.6.
59
Рис. 3.6. Результат работы программы с использованием функции Split.
Управляющие последовательности и специальные символы в строках При выводе на экран строк в C# можно использовать управляющие последовательности, позволяющие управлять процессом вывода текстовой информации на экран. Основной сферой применения таких последовательностей является вывод тех символов, которые невозможно вывести на экран при помощи обычной стандартной строки, заключенной в кавычки. Список основных управляющих последовательностей приведен ниже. \’ \” \\ \a \b \f \n \r \t
Одинарная кавычка Двойная кавычка Обратный слэш Системное оповещение Backspace Начало следующей страницы Начало новой строки Return Горизонтальный символ табуляции
Не все из этих символов допустимо использовать в строках Web приложения. Тем не менее, на рис. 3.8 отображен результат выполнения следующей команды. string esc = "Имя программы \"Visual Studio 2005\", метонахождение: C:\\Program Files\\Microsoft Visual Studio 8\\";
60 Label1.Text = esc;
Рис. 3.7. Пример отображения строки с использованием управляющих символов.
Кроме управляющих последовательностей в C# предусмотрен специальный префикс @, предназначенный для дословного вывода строки вне зависимости от наличия в ней управляющих символов. Можно сказать, что этот префикс отключает присутствующие в строке управляющие символы. Использование класса System.Text.StringBuilder При работе со строками в C# необходимо учитывать то, что строки являются неизменяемыми. Все действия, направленные на изменение строк, на самом деле не изменяют исходный ее вариант. Они лишь возвращают измененную копию строки. Это можно увидеть на следующем примере. string s1 = "Пример строки"; string s2 = s1.ToUpper(); Response.Write(s1); Response.Write(""); Response.Write(s2);
В данном случае строка s1 не претерпела никаких изменений. Метод ToUpper() создал копию строки s1 и применил к ней необходимые преобразования, поэтому на экран будет выведена как исходная строка, так и строка, подвергшаяся изменению. Такое же утверждение справедливо и для обычной операции конкатенации строк. Результатом выполнения операции конкатенации строк является также новая строка. В ряде случаев следует избегать ситуаций, когда в результате выполнения операции создается новая строка, т.к. это неизбежно связано с дополнительными накладными расходами памяти и других ресурсов компьютера при выполнении операции. C# содержит специальный класс StringBuilder, используя который можно избежать создания копий строк при их обработке. Все изменения, вносимые в объект данного класса немедленно отображаются в нем, что в целом ряде случаев гораздо эффективнее, чем работа с множеством копий строки. Основной операцией, чаще всего используемой классом StringBuilder является операция добавления к строке содержимого. Для этого существует
61
метод Append. Следующий код добавляет одну строку к другой и результат выводит в окно браузера. При этом изменяется оригинал строки, копия не создается. StringBuilder sb = new StringBuilder("Ехали медведи на велосипеде"); sb.Append(", а за ними кот задом наперед"); Response.Write(sb);
Кроме добавления, класс StringBuilder содержит множество других методов, наиболее значимые из которых перечисленных ниже. После того, как все необходимые действия, связанные с обработкой строки были выполнены, необходимо вызвать метод ToString() для перевода содержимого объекта в обычный тип данных string. Append AppendFormat
CopyTo Insert Remove Replace
Добавление заданной строки в конец строки объекта. Добавление заданной форматированной строки (строки, содержащей управляющие символы) в конец строки объекта. Копирование символов заданного сегмента строки в заданные ячейки массива символов. Добавление строки в заданную позицию строки объекта. Удаление заданного количества символов из строки объекта Замена заданного символа, либо строки объекта на другой заданный символ, либо строку.
При интенсивной работе со строками, рекомендуется использовать класс StringBuilder, т.к. это позволяет уменьшить накладные расходы, связанные с созданием копии строки при выполнении каждой операции. Процедуры и функции Процедуры и функции C# практически полностью соответствуют их определениям в языке C++. Фактически, в языке С отсутствует определение процедуры, принятой в других языках программирования. Если определить функцию, которая не возвращает никакого результата (тип void) в вызывающую программу, мы получим аналог процедуры в других языках программирования. Основные отличия процедуры от функции состоит в том, что функция должна всегда возвращать некоторый результат, и, кроме того, должна быть вызвана в выражении. Процедура же не возвращает никакого результата, ее вызов осуществляется с помощью оператора языка, кроме того, она имеет входные и выходные аргументы, причем выходных аргументов может быть достаточно много. Таким образом, создавать процедуру целесообразно в том случае, когда необходимо произвести какиелибо вычисления, в результате которых должен быть получен результат в виде одного значения. Процедуру – соответственно в случае, когда может быть получении результат, представляющий собой набор каких-либо значений.
62
Тем не менее, существует ряд нововведений, касающихся функций, появившихся в C#. Наиболее важной особенностью использования функций и процедур в C# является то, что, так как C# является полностью объектноориентированным языком программирования, функции не могут существовать в отрыве от классов. Поэтому функцию библиотек процедур и функций в C# выполняют библиотеки классов. Так как различия между процедурами и функциями достаточно условны, а существовать они могут только внутри классов, в дальнейшем в качестве синонима процедур и функций будем употреблять термин метод. Синтаксис описания методов достаточно прост: [атрибуты][модификаторы]{void | тип результата функции} имя метода ([список формальных аргументов])
Имя метода и список формальных аргументов представляют сигнатуру метода. Квадратные скобки, как это принято, показывают, что их содержимое может быть опущено при описании метода. Атрибуты и модификаторы являются очень важной составляющей описания любого метода, тем не менее, они будут рассмотрены при описании классов, т.к. имеют к этому самое непосредственное отношение. Пока же можно считать, что под модификаторами подразумеваются модификаторы доступа из которых мы будем рассматривать пока только два: private и public. Private означает, что данный метод является закрытым, соответственно доступ к нему могут получить только методы того класса, в котором он объявлен. Public – наоборот, означает, что доступ к данному методу является открытым и общедоступным из любой точки приложения. При определении метода обязательным является указание типа возвращаемого значения, имени метода, а также круглых скобок. В случае, если метод не должен возвращать какого-либо значения в вызывающую программу, указывается тип void, являющийся признаком принадлежности метода к разряду функций. Простейшими примерами описания функций являются следующие. private void A() {} public int B() {} public long Stepen(int a, int b) { long r; r = (long)Math.Pow(a, b); return (r); }
Здесь метод А является закрытой процедурой, методы В и Stepen – открытыми функциями. У методов А и В не определены формальные параметры, в то время как у метода Stepen два формальных параметра a и b.
63
Как видно из этого примера, список формальных аргументов может быть пустым, что является типичной ситуацией для методов класса. Однако, формальные аргументы, определяемые для метода могут содержать и некоторые дополнительные атрибуты, влияющие на их поведение. Полный синтаксис объявления формального аргумента выглядит следующим образом. [ref|out|params] тип_аргумента имя_аргумента
При этом обязательным является указание типа аргумента, который может быть скалярным, массивом, классом, структурой, словом любым типом, допустимым в C#. Иногда при вызове метода возникает необходимость передавать в метод произвольное количество фактических аргументов, несмотря на фиксированное количество аргументов, содержащееся в его определении. Для реализации такой возможности необходимо задать ключевое слово params. Оно задается один раз и указывается только для последнего аргумента списка, объявляемого как массив произвольного типа. При вызове метода этому формальному аргументу соответствует произвольное количество фактических аргументов. Все аргументы методов можно разделить на три группы: входные, выходные, обновляемые. Входные необходимы для передачи информации методу, их значения в теле метода доступны только для чтения. Выходные – представляют собой результаты метода, они получают значения в ходе работы метода. Обновляемые способны выполнять обе функции. Выходные аргументы должны быть помечены ключевым словом out, при этом, в теле метода должен обязательно присутствовать оператор присваивания значения этому аргументу, обновляемые аргументы помечаются с помощью ключевого слова ref. В качестве примера слегка модернизируем описанный ранее метод Stepen. public void Stepen(out long r,int a, int b) { r = (long)Math.Pow(a, b); }
Как видно из примера, функция Stepen была фактически преобразована в процедуру (тип возвращаемого функцией значения был изменен на void). Также был добавлен формальный аргумент r, используемый в качестве выходного. В теле метода этому аргументу присваивается значение, которое впоследствие может быть использовано в вызывающей программе. Вызов этого метода может быть осуществлен следующим образом. long s; Stepen(out s, 2, 6);
64 Response.Write(s.ToString());
Обратите внимание на то, что при вызове этого метода первый параметр также указывается с ключевым словом out. Из примера видно, что при завершении работы метода Stepen и возврата в вызывающую программу, происходит передача значения переменной r из метода в переменную s, находящуюся в адресном пространстве основной программы. Рассмотрим пример передачи произвольного количества значений исходных данных в метод для их обработки. Как уже упоминалось выше, для этого необходимо использовать ключевое слово params. Создадим пример, в котором в метод Stepen может передаваться произвольное количество чисел для нахождения суммы их квадратов. Объявление метода в этом случае выглядит следующим образом. public void Stepen(out long r, int a, params int[] b) { r = 0; foreach (int i in b) r += (long)Math.Pow(i, a); }
Вызов метода может быть осуществлен следующим образом. int[] digits ={1,8,4}; Stepen(out s, 2, digits); Response.Write(s.ToString());
Видно, что для вызова метода необходимо сформировать массив целых чисел, который затем передать в качестве третьего аргумента в метод. Результат вычисления суммы накапливается в переменной r, а затем передается в переменную s вызывающей программы. Результат работы программы показан на рис. 3.8.
Рис. 3.8. Результат выполнения метода Stepen для расчета значения суммы квадратов чисел.
Иногда возникает необходимость не только передавать произвольное количество исходных данных для расчета, но и получать из вызывающей процедуры произвольное количество переменных, содержащих результаты расчета.
65
Рассмотрим пример создания процедуры, позволяющей получать массив чисел, возводить каждое из них в определенную степень, передавать результат расчета в вызывающую программу и выводить результат на экран. Процедура в этом случае будет выглядеть следующим образом. public void Stepen(out long[] r,int a, params int[] b) { r=new long[0]; Array.Resize(ref r,b.Length); int j=0; foreach (int i in b) r[j++] = (long)Math.Pow(i, a); }
Здесь необходимо отметить следующие важные особенности. Вопервых, в качестве параметра, возвращающего значение в вызывающую программу использован массив чисел типа long. Во-вторых, прежде чем становится возможным использовать этот массив, его размер необходимо привести в соответствие с размером массива b. Для этого можно воспользоваться методом Resize объекта Array. Однако, этот метод позволяет изменять количество элементов такого массива, для которого это количество уже определено, поэтому перед вызовом метода Resize создается новый массив r, состоящий из ноля элементов. Стоит отметить также то, что перед первым параметром метода Resize находится ключевое слово ref, говорящее о том, что данный метод принимает ссылку на массив r, именно поэтому внутри метода становится возможным изменение параметров самого массива, а не его копии. Вызов метода можно осуществить следующим образом. Response.Write("Результаты вычисления значений массива: "); long [] result; int [] data={2,3,4,5}; Stepen3(out result, 2, data); foreach (long i in result) Response.Write(i.ToString()+" ");
Результат работы программы изображен на рис. 3.9.
Рис. 3.9. Результат работы метода, возвощящего в заданную степень массив чисел.
66
Подытоживая все сказанное выше относительно методов, необходимо обратить внимание на следующее. Язык C# является объектноориентированным языком программирования. Как известно, основную роль в таких языках играют ссылочные типы, поэтому когда методу передается объект ссылочного типа, все поля этого объекта могут меняться в методе, т.е. программный код метода может получить полный доступ ко всем полям объекта. Это происходит несмотря на то, что формально объект не является выходным и не имеет ключевых слов ref и out, т.е. использует семантику вызова по значению. При таком вызове сама ссылка на объект остается неизменной, но состояние объекта, значения его полей могут полностью измениться. Такая ситуация достаточно распространена и является типичной, поэтому при работе со ссылочными типами данных, ключевые слова ref и out не часто появляются при описании аргументов метода. Классы и структуры Классы Не претендуя на полноту изложения положений объектноориентированного анализа и проектирования, постараемся разобрать один из аспектов практического применения его принципов. За дополнительной информацией относительно особенностей объектного программирования в C# рекомендуется познакомиться с [3]. Язык C# в полной мере является объектно-ориентированным. Из представленного выше материала видно, что все действия, осуществляемые в C# являются обращениями к ресурсам неких классов. Иногда для этого необходимо создавать объекты классов, иногда (при вызове статических методов) создание объектов не требуется. Одной из важнейших особенностей C# является то, что здесь нельзя создать отдельно стоящую процедуру или функцию, она обязательно должна входить в какой-либо класс. Данный раздел ставить своей целью рассмотрение основных особенностей и базовых принципов использования объектноориентированного программирования в C#. Предполагается, что читатель обладает набором необходимых знаний основ объектно-ориентированного программирования. Итак, класс – это шаблон, который определяет свойства и методы объектов, создаваемых как экземпляры данного класса. Основное назначение класса – создавать некий тип данных, задающий реализацию некоторой абстракции данных, характерной для задачи, в интересах которой создается программная система. Таким образом, классы – не просто кирпичики, из которых строится система. Каждый кирпичик теперь имеет важную содержательную начинку, отвечающую за выполнение своей операции. Все современные программные системы создаются с использованием принципов объектно-ориентированного программирования. Это означает, что любая сущность, с которой осуществляются какие-либо действия должна
67
быть описана в виде класса. Таким образом система представляет собой совокупность взаимодействующих друг с другом классов. Определение класса в C# практически идентично определению такого же класса в C++. Тем не менее, в C# существуют дополнительные элементы, значительно облегчающие практическое использование классов. Рассмотрим основные правила и примеры построения и использования классов. Предполагая, что читатель знаком с основами объектного программирования на C++, рассмотрим пример создания и использования классов с помощью языка C#. Предположим, для системы обработки банковских операций, осуществляемых клиентами, необходимо создать класс Client, реализующий всю необходимую функциональность, связанную с клиентом. В качестве учебного примера создадим класс Client, содержащий информацию об имени, паспорте и дате рождения заемщика, свойства для доступа к этим данным, а также метод, с помощью которого возможно редактировать информацию о клиенте. Определение класса начнем с объявления его имени, а также задания его полей и необходимых методов (конструкторов). Определение класса будет выглядеть следующим образом. public class Client { private string Name; private string Passport; private DateTime BirthDate; public Client() { }
}
Здесь определены поля класса, а также конструктор по умолчанию (без параметров). Для классов C# можно определить любое количество конструкторов. Если в классе не определен ни один конструктор, создается конструктор по умолчанию, который при создании объекта автоматически присвоит всем переменным-членам класса безопасные значения по умолчанию. В данном случае, конструктор по умолчанию задается явно. Область видимости полей класса в соответствии с правилами должна быть определена либо как закрытая, либо защищенная (в случае, если предполагается наследование его полей). Доступ же к полям-членам класса должен быть организован либо посредством методов, либо с помощью свойств класса. Ранее, в языке C++ свойства отсутствовали, однако они позволяют организовывать очень простой доступ к полям-членам класса. Создадим свойства класса Client, обеспечивающие чтение и запись значений полей класса. public string passport {
68 get { } set { }
}
return Passport;
Passport = value;
public string name { get { return Name; } set { Name = value; } } public int age { get { int a; a = DateTime.Now.Year - BirthDate.Year; return a; } } public DateTime birthdate { get { return BirthDate; } set { if (DateTime.Now > value) BirthDate = value; else throw new Exception("Введена не верная дата рождения"); } }
Как видно из данного примера, свойство состоит из методов set и get. При этом, свойство должно содержать хотя бы один из методов. Set позволяет изменять значение поля класса, get – получать значение. В метод Set передается значение параметра с помощью переменной value. Оба метода могут содержать произвольное количество операторов, описывающих алгоритм выполнения действий в процессе чтения или записи значения в поле класса. В данном примере свойства passport и name позволяют просто получить доступ к полям класса считывая или устанавливая значения соответствующих переменных. Свойство birthdate также предназначено для чтения и записи значения переменной-члена класса BirthDate. При этом, при
69
чтении значения (операция get) происходит просто передача значения переменной BirthDate, при попытке же записи нового значения в эту переменную, происходит проверка допустимости устанавливаемого значения переменной. В данном случае проверка сводится к сравнению нового значения даты рождения с текущей датой. Если устанавливаемое значение даты рождения больше, либо равно текущей дате, генерируется исключение, которое не позволяет записать новое значение в переменную-член класса. Свойство age предназначено для получения текущего возраста клиента. Оно предназначено только для чтения значения из переменной, поэтому содержит лишь метод get. При использовании свойства age происходит вычисление текущего значения возраста клиента в годах путем вычитания года рождения из текущего значения года. Использование свойств аналогично использованию переменных. В следующем примере создается объект c1 класса Client. Затем поля данного объекта заполняются значениями с использованием свойств. После этого, на экран выводятся значения полей, для этого также используются свойства класса. Client c1=new Client(); c1.name = "Вася"; c1.passport = "9002"; c1.birthdate = new DateTime(1967, 08, 03); Response.Write("Имя=" + c1.name+""); Response.Write("Паспорт=" + c1.passport+""); Response.Write("Возраст="+c1.age);
Помимо переменных-членов класса и свойств для доступа к этим переменным, практически любой класс содержит методы, предназначенные для описания алгоритмов выполнения классом действий. Один из обязательных методов класса – конструктор по умолчанию, уже был показан ранее. Однако, классы в C# могут содержать произвольное количество конструкторов, предназначенных для инициализации объекта данного класса. В качестве примера, создадим конструктор с параметрами, позволяющий инициализировать объект класса введя значения полей этого объекта в качестве параметров конструктора. Сделать это можно следующим образом. public Client(string ClientName, string ClientPassport, DateTime ClientBirthDate) { name = ClientName; passport = ClientPassport; birthdate = ClientBirthDate; }
Видно, что конструктор содержит три параметра, пояснять смысл которых не требуется. В теле конструктора происходит запись переданных в качестве параметров значений в соответствующие поля класса посредством свойств данного класса. В случае с датой рождения это позволяет не
70
дублировать процедуру проверки введенной даты, а воспользоваться алгоритмом, реализованным в свойстве birthdate. Создадим также метод, позволяющий изменить значения полей объекта класса Client. public void EditClient(string ClientName, string ClientPassport,DateTime ClientBirthDate) { Name = ClientName; Passport = ClientPassport; birthdate = ClientBirthDate; }
Как видно из примера, исходный код этого метода практически полностью идентичен конструктору с параметрами с разницей в имени, а также типе возвращаемого значения. Конечно, в данном случае можно было бы обойтись и использованием свойств для изменения значений полей класса, однако, иногда бывает полезно, чтобы такого рода изменения были реализованы в рамках одного метода, тем более, если алгоритм изменений является нестандартным. Теперь, с использованием конструктора с параметрами, можно создать и сразу же инициализировать объект класса Client. Client c1=new Client("Вася","9002",new DateTime(1967,08,03));
Структуры Во многих отношениях структуры можно рассматривать как особую разновидность классов. Для структур можно определять конструкторы, реализовывать интерфейсы. Однако, для структур в C# не существует базового класса, поэтому все структуры являются производными от типа ValueType. Простейший пример структуры можно представить следующим образом. public struct Employee { public string name; public string type; public int deptID; }
Использование структуры возможно следующим образом. Сначала ее необходимо создать. В момент создания структуры, для нее выделяется память в области стека. В дальнейшем к элементам структуры возможно обращение путем указания имени структуры и имени элемента, разделенных точкой. Employee Alex; Alex.name = "Alex";
71 Alex.type = "manager"; Alex.deptID = 2310;
В реальной системе для более удобного использования структур целесообразно определить конструктор или несколько конструкторов. При этом необходимо помнить, что в структурах невозможно переопределить конструктор по умолчанию (без параметров). Все определенные в структуре конструкторы должны принимать параметры. Ниже представлен пример конструктора структуры. public Employee(int DeptID, string Name, string EmpType) { deptID = DeptID; type = EmpType; name = Name; }
Использование конструктора выглядит следующим образом. Employee Nick=new Employee(278,"Nick","worker");
Как видно, для вызова конструктора структуры необходимо использовать ключевое слово new. Аналогичным образом становится возможным создание и использование методов структур. Коллекции Массивы – это удобное средство организации хранения и упорядочивания информации. Однако, при работе с массивами практически всегда возникают задачи, связанные с обработкой данных, содержащихся в них. В современном программировании очень часто элементами массива являются объекты, над которыми необходимо выполнять различные действия. В силу особенностей коллекций, а также учитывая тот факт, что упор при изложении данного материала был сделан на использование коллекция для хранения именно объектов, раздел посвященный коллекциям расположен после раздела, посвященного классам и структурам. Ранее был рассмотрен класс Array, в котором реализован целый набор часто используемых операций над элементами массива, такие как сортировка, клонирование, перечисления и расстановка элементов в обратном порядке. Тем не менее, при работе с массивами возникает и целый ряд других задач. Решением этих задач и занимаются коллекции, позволяющие организовывать элементы специальным образом и производить в последствии над ними определенный набор операций. Все определенные в .NET Framework коллекции расположены в пространстве имен System.Collections. Наиболее часто используемые коллекции этого пространства имен представлены ниже.
72 ArrayList Hashtable Queue SortedList
Stack
Массив объектов, динамически изменяющий свой размер. Набор взаимосвязанных ключей и значений, основанных на хэш-коде ключа. Стандартная очередь, реализованная по принципу «первым вошел, первым вышел» (FIFO). Представляет класс, элементами которого могут быть пары ключ-значение отсортированные по значению ключа и предоставляющие доступ к элементам по их порядковому номеру (индексу). Очередь, реализованная по принципу «первым вошел, последним вышел» (LIFO).
В пространстве имен System.Collections.Specialized расположены классы, предназначенные для использования в специальных случаях. Так, класс StringCollection представляет набор строк. Класс StringDictionary представляет собой набор строковых значений, состоящих из пар ключзначение, в качестве значений которых используются строка. В данном занятии будут рассмотрены общие вопросы применения коллекций. Для более детального изучения их особенностей и возможностей, рекомендуется ознакомиться с разделом Интерфейсы, а также использовать дополнительную литературу и справочную систему MSDN. Рассмотрим пример использования коллекций на примере ArrayList. Для этого воспользуемся рассмотренным ранее классом Client. Очень часть возникает ситуация, при которой необходимо бывает помещать объекты в список или массив для их дальнейшей обработки. Конечно, при этом можно воспользоваться теми возможностями, которые предоставляют стандартные массивы C#, рассмотренные ранее. Однако, коллекции, как уже было сказано, обладают более широкими возможностями и, следовательно, их применение более предпочтительно по сравнению с массивами. Так, для обработки данных о клиентах создадим класс Clients, предназначенный для хранения списка клиентов и предоставляющий возможности для их обработки. При этом, необходимо учесть, что класс Clients должен обеспечивать возможность добавления, удаления, нумерации элементов (клиентов). Для реализации этих возможностей необходимо использовать класс ArrayList в качестве вложенного в класс Clients. Таким образом, нам необходимо определить с данном классе элемент, позволяющий вести список клиентов, а также реализовать набор открытых методов, которые будут передавать вызовы на выполнение различных действий внутреннему классу, производному от ArrayList. Пример исходного кода класса Clients приведен ниже. public class Clients { private ArrayList ClientsList; public Clients()
73 {
}
ClientsList=new ArrayList(); } public int ClientsCount { get { return ClientsList.Count; } } public void AddClient(Client c) { ClientsList.Add(c); } public void RemoveClient(int ClientToRemove) { ClientsList.RemoveAt(ClientToRemove); } public Client GetClient(int ClientID) { return (Client)ClientsList[ClientID]; }
Использование такого класса представляется очень простым. Clients cl=new Clients(); cl.AddClient(new Client("Сидоров","9002",new DateTime(1980,12,21))); cl.AddClient(new Client("Петров","9004",new DateTime(1975,03,15)));
Теперь становится возможным обращение к любому классу помещенному в коллекцию Clients посредством метода GetClient.
Результатом работы этого фрагмента программы будет строка Петров. Тем не менее, существует несколько неудобств от использования класса Clients в его нынешней реализации. Во-первых, более привычным обращением к элементу массива, либо списка в языке C# является использование индексаторов, обозначаемых в виде квадратных скобок. Для реализации этой возможности в классе Clients необходимо создать индексатор. public Client this[int pos] { get { return ((Client)ClientsList[pos]); } set { ClientsList[pos] = value; } }
74
Как видно, от обычного метода индексатор отличается именем (this), а также наличием квадратных скобок, в которых указывается параметр – номер извлекаемого элемента. Теперь посредством индексатора становится возможным извлечение элемента из коллекции, а также запись элемента в коллекцию под определенным номером. Response.Write(cl[1].name); cl[1].name = "Сидоров"; Response.Write(cl[1].name);
Теперь становится возможной организация обработки элементов массива в цикле. При работе с коллекциями довольно удобной является возможность использования цикла forech. Для реализации такой возможности при работе с классом Clients необходимо реализовать метод GetEnumerator() интерфейса IEnumerator. Для этого необходимо реализовать наследование классом Clients интерфейса IEnumerator и реализовать метод как показано ниже. public class Clients:IEnumerable { public IEnumerator GetEnumerator() { return ClientsList.GetEnumerator(); } }
Пример использования цикла показан ниже.
foreach
с использованием класса
foreach (Client c in cl) { Response.Write("Имя="+c.name+" "); Response.Write("Паспорт="+c.passport+" "); Response.Write("Возраст=" + c.age); Response.Write(""); }
Clients
75
Занятие 4.Основы Web программирования на основе ASP.NET Ранее, в занятии 2 был рассмотрен пример создания простого Web приложения, содержащего кнопку, при нажатии на которую на экране отображается строка «Это простейшее приложение ASP.NET». Для более глубокого понимания кода, созданного ASP.NET в процессе обработки события нажатия на кнопку, просмотрим исходный код страницы, отображенной в браузере. Для этого необходимо вызвать контекстное меню страницы, отображенной в браузере и выбрать пункт Просмотр HTML кода. Текст HTML кода страницы приведен ниже. Untitled Page