Архивы рубрики: Алгоритмы

Основы работы с Windows API (WinAPI)

Введение.
Цель этого обзора — помочь человеку перейти от использования средств Delphi к функциям Win API. Предполагается, что читатель уже неплохо владеет Delphi, поэтому будет подчёркиваться, прежде всего, разница между этими двумя инструментами. Кроме того, многие авторы книг по Delphi не уделяют достаточно внимания функциям Win API, предназначенным для работы с окнами и графикой, потому что считают, что VCL Delphi достаточно хорошо справляется с этими задачами. Так что часто приходится учиться работе с Win API по книгам по 16-разрядному Borland Pascal’ю. Поэтому я буду обращать внимание и на отличие 32-разрядных версий от 16-разрядных. Но я не буду без особой необходимости останавливаться на подробном описании конкретных функций, так как это всё сделано в справочной системе. Я также остановлюсь и на этой самой справочной системе, потому что начинающему программисту может оказаться не очень просто разобраться с ней.

Что такое Win API
Win API — это набор функций, предоставляемых операционной системой каждой программе. Эти функции находятся в стандартных динамически компонуемых библиотеках (Dynamic Linked Library, DLL), таких как kernel32.dll, user32.dll, gdi32.dll. Эти файлы находятся в директории Window\System. Вообще говоря, каждая программа должна самостоятельно заботится о том, чтобы подключить эти библиотеки. DLL могут подключаться к программе статически и динамически. В первом случае программа «освобождает» DLL только при завершении, во втором освобождение может произойти в любой момент. Если после освобождения DLL оказывается, что её больше не использует ни одна программа, она выгружается из памяти. Так как стандартные библиотеки используются самой системой, они всегда находятся в памяти, и поэтому использование динамического подключения бессмысленно. Чтобы статически подключить в Delphi некоторую функцию Win API, например, функцию GetWindowDC из модуля user32.dll, надо написать конструкцию вида

function GetWindowDC(Wnd: HWnd); HDC; stdcall; external ‘user32.dll’ name ‘GetWindowDC’;

Такая запись обеспечивает одновременно и статическое подключение библиотеки user32, и декларацию функции GetWindowDC, что позволяет компилятору в дальнейшем работать с ней. Обратите внимание, что все функции Win API написаны в соответствии с моделью вызова stdcall, а в Delphi по умолчанию используется другая модель — register (модель вызова определяет, как функции передаются параметры). Поэтому при импорте функций из стандартных библиотек необходимо явно указывать эту модель. Далее указывается, из какой библиотеки импортируется функция и какое название в ней она имеет. Дело в том, что имя функции в библиотеке может не совпадать с тем, под которым она становится известна компилятору. Позже я остановлюсь на тех случаях, когда это используется. Главным недостатком DLL следует считать то, что в них сохраняется информация только об именах функций, но не об их параметрах. Поэтому, если при импорте функции указать не те параметры, какие подразумевались автором DLL, то программа будет работать неправильно (вплоть до зависания), а ни компилятор, ни операционная система не смогут указать на ошибку.

Обычно программа использует довольно большое число функций Win API. Декларировать их все довольно утомительно. К счастью, Delphi избавляет программиста от этой работы: все эти функции уже описаны в соответствующих модулях, достаточно упомянуть их имена в разделе uses. Например, большинство общеупотребительных функций описаны в модулях Windows.dcu и Messages.dcu.

Как получить справку по функциям Win API
Для тех, кто решил использовать Win API, самым необходимым инструментом становится какая-либо документация по этим функциям. Их так много, что запомнить все совершенно нереально, поэтому работа без справочника под рукой просто невозможна. Наиболее доступным справочником для российского программиста является Win32 Developer’s Reference, справочная система фирмы Microsoft, потому что фирма Inprise (тогда ещё Borland), получила лицензию на включение её в состав Delphi. Сам я буду постоянно ссылаться на эту справку, потому что подробное описание функций займёт слишком много места, да и нет особого смысла описывать то, что описали и без меня.

Хотя в комплект поставки Delphi и входит эта справочная система, содержащая описание всех функций Win API, получение справки по ним не настолько удобное, как по стандартным функциям Delphi. Если набрать в редакторе Delphi имя какой-нибудь функции Win API, поставить курсор в начало этой функции и нажать F1, то откроется справка по ней, как и в случае обычных функций и процедур. Однако функции Win API не появляются в предметном указателе справочной системы. Авторы Delphi объясняют это ограничениями, накладываемыми самой справочной системой (как обычно, всех собак вешают Windows). Они же советуют создать в меню кнопки «Пуск» ярлык к справочной системе Win32 Developer’s Reference. Мне остаётся только присоединиться к этому совету и добавить, что ярлык надо создавать к файлу MSTools.hlp, который находится в директории $(Delphi)\Help.

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

WCHAR = WideChar;
LPSTR = PChar;
LPCSTR = PChar;
LPWSTR = PWideChar;
LPCWSTR = PWideChar;
DWORD = Integer;
BOOL = LongBool;
PBOOL = ^BOOL;
PINT = ^Integer;
PWORD = ^Word;
PDWORD = ^DWORD;
LPDWORD = PDWORD;
UCHAR = Byte;
PUCHAR = ^Byte;
SHORT = Smallint;
UINT = Integer;
PUINT = ^UINT;
ULONG = Longint;
PULONG = ^ULONG;
LCID = DWORD;
LANGID = Word;

int = Integer;
long = LongInt;
PVOID = Pointer;
HANDLE = THandle;
LPPOINT = TPoint;
RECT = TRect;
LPRECT = PRect;
LPSIZE = PSize;
BITMAP = TBitmap;

Все типы, приведённые в первой части таблицы, в целях совместимости описаны в модуле Windows.dcu, поэтому их можно использовать наравне с обычными типами Delphi. Кроме этих типов общего назначения существуют ещё специальные. Например, дескриптор окна имеет тип HWND, первый параметр сообщения — тип WPARAM. Эти специальные типы также описаны в Windows.dcu. В некоторых имена типов, встречающихся в справке, и соответствующих им типов из Windows.dcu отличаются только добавлением буквы «T», как это можно видеть из второй части таблицы. Кстати, не следует путать тип TBitmap, определённый в Windows.dcu, с классом TBitmap, определённым в Graphics.dcu. Зачем разработчикам Delphi потребовалось называть разные типы одним именем, не понятно, тем более что во второй версии Delphi был тип BITMAP, который куда-то исчез в третьей. Зато в четвёртой версии снова появился BITMAP, остался TBitmap, да ещё добавился tagBITMAP, и все эти три типа означают то же самое.

Теперь о синтаксисе описания самой функции в Си. Оно имеет вид
( , , …);

Еще в Си различается верхний и нижний регистр, поэтому идентификаторы HDC, hdc, hDC и т. д. — разные идентификаторы (автор Си очень любил краткость и хотел, чтобы можно было делать не 26, а 52 переменные с именем из одной буквы). Поэтому часто можно встретить, что имя параметра и его тип совпадают с точностью до регистра. К счастью, при описании функции в Delphi мы не обязаны сохранять имена параметров, значение имеют лишь их типы и порядок следования. С учётом всего этого функция, описанная в справке как

HMETAFILE CopyMetaFile(HMETAFILE hmfSrc, LPCTSTR lpszFile);

в Delphi имеет вид

function CopyMetaFile(hmfSrc: HMETAFILE; lpszFile: LPCTSTR): HMETAFILE;

или, что то же самое,

function CopyMetaFile(hmfSrc: HMetaFile; lpszFile: PChar): HMetaFile;

Так как в Паскале нет разницы, написать, например, HMETAFILE или HMetaFile, в дальнейшем я буду придерживаться второго варианта, потому что так легче читать, хоть это и не совпадает с тем, как принято в справочной системе.

Несколько особняком стоит тип VOID. Если функция имеет такой тип, то в Паскале она описывается как процедура. Если вместо параметров у функции в скобках стоит VOID, это означает, что функция не имеет параметров. Например, функция

VOID CloseLogFile(VOID);

в Delphi описывается как

procedure CloseLogFile;

Не путайте VOID и PVOID. PVOID — это нетипизированный указатель, соответствующий типу Pointer.

В тех случаях, когда тип параметра является указателем на другой тип (обычно начинается с букв LP), при описании этой функции в Delphi можно пользоваться параметром-переменной, так как в этом случае функции передаётся указатель. Например, функция

int GetRgnBox(HRGN hrgn, LPRECT lprc);

в файле Windows.pas описана

function GetRgnBox(RGN: HRGN; var p2: TRect): Integer;

И, наконец, если не удаётся понять, как функция, описанная в справке, должна быть переведена на Паскаль, можно попытаться найти описание этой функции в исходных текстах модулей, поставляемых вместе с Delphi. Эти модули находятся в директории $(DELPHI)\Source\RTL\Win. Можно также воспользоваться подсказкой, которая всплывает в редакторе Delphi после того, как будет набрано имя функции.

Если посмотреть справку, например, по функции GetSystemMetrics, то видно, что эта функция должна иметь один целочисленный параметр. Однако далее в справке предлагается при вызове этой функции подставлять в качестве параметра не числа, а SM_ARRANGE, SM_CLEANBOOT и т. д. Подобная ситуация и со многими другими функциями Win API. Все эти SM_ARRANGE, SM_CLEANBOOT и т. д. являются именами числовых констант. Эти константы описаны в том же модуле, в котором описана функция, использующая их, поэтому можно не выяснять численные значения этих констант, а указывать при вызове функций их имена, например, GetSystemMetrics(SM_Arrange); Если по каким-то причинам всё-таки потребовалось выяснить численные значения, то в справочной системе их искать не стоит — их там нет. Я могу только опять отправить к исходным текстам модулей Delphi, в которых эти константы описаны. Так, например, просматривая Windows.pas, можно узнать, что SM_ARRANGE = 56. Кстати, всем, кто решиться самостоятельно просматривать исходники, я очень рекомендую использовать для этого не текстовый редактор, а программу, которая может только показать, но не изменить файл (что-то вроде просмотра по F3 в Norton Commander’е). Так безопаснее. Или же стоит подумать о резервной копии.

В описании многих функций Win API вверху можно увидеть три ссылки: QuickInfo, Overview и Group. Первая даёт краткую информацию о функции. Самой полезной частью этой информации является то, для каких версий Windows эта функция реализована. Например, очень полезна функция MaskBlt, однако QuickInfo показывает, что она реализована только в Windows NT. Программа, использующая эту функцию, не будет работать в Windows 95. Иногда напротив названия одной из систем стоит слово «Stub», которое переводится как «пень», «обрубок» (например, для функции GetDeviceGammaRamp это слово стоит напротив Windows NT). Это означает, что в данной версии эта функция присутствует (то есть обращение к ней не вызывает ошибки), но ничего не делает. Оставим на совести программистов из Microsoft вопрос, зачем нужны такие пни. Overview — это краткий обзор какой-то большой темы. Например, для любой функции, работающей с растровыми изображениями, обзор будет в двух словах объяснять, зачем в принципе нужны эти самые растровые изображения. Судя по непроверенным данным, первоначально эти обзоры замышлялись как нечто большее, но потом остановились на таких вот лаконичных фразах. Как бы то ни было, найти в обзоре полезную информацию удаётся крайне редко, поэтому заглядывать туда стоит только если ну совсем ничего не понятно. И, наконец, Group. Эта ссылка приводит к списку всех функций, родственных данной. Например, для функции CreateRectRgn группу будут составлять все функции, имеющие отношение к регионам. Если теперь нажимать на кнопку << (два знака «меньше») сразу под главным меню окна справки, то будут появляться страницы с кратким описанием возможных применений объектов, с которыми работают функции (в приведённом примере описание возможностей регионов). Чтобы читать их в нормальной последовательности, лучше всего нажать на ;. Иногда в справке можно встретить указания «Windows 95 only» или «Windows NT only». К этим замечаниям следует относится критически, так как справка написана для Windows 95, когда ещё не было Windows NT 4.0, описывается версия со старым интерфейсом. Так что то, про что написано «Windows 95 only», может вполне успешно работать и в Windows NT 4.0 и выше, особенно если это «что-то» связано с пользовательским интерфейсом. То же самое относится и к QuickInfo. Такие вещи лучше всего проверять на практике. Ещё несколько слов о числовых константах. В справке можно встретить числа вида, например, 0xC56F или 0x3341. Префикс «0x» в Си означает шестнадцатеричное число. В Delphi надо его заменить на «$», то есть вышеназванные числа должны быть записаны как $C56F и $3341 соответственно. Дескрипторы вместо классов
Программируя в Delphi, мы быстро привыкаем к тому, что каждый объект реализуется экземпляром соответствующего класса. Например, кнопка реализуется экземпляром класса TButton, контекст устройства — классом TCanvas. Но когда создавались первые версии Windows, объектно-ориентированный метод программирования ещё не был общепризнанным, поэтому он не был реализован. Современные версии Windows частично унаследовали этот недостаток, поэтому в большинстве случаев приходится работать по старинке, тем более что DLL могут экспортировать только функции, но не классы.

Когда мы создаём некоторый объект в Windows, ему присваивается уникальный 32-разрядный номер, называемый дескриптором. В дальнейшем при работе с этим объектом каждой функции передаётся этот дескриптор. В этом и заключается главное различие между методами класса и функциями Win API. Первые связаны с тем экземпляром класса, через который они вызыва-ются, и поэтому не требуют явного указания на объект. Вторым необходимо такое указание, так как они сами по себе никак не связаны ни с одним объектом.

Не следует думать, что при работе с Win API следует полностью отказываться от классов Delphi. Эти методы прекрасно работают вместе. Правда, внутренние механизмы Delphi не могут включиться, если изменение объекта происходит через Win API. Например, если спрятать окно не с помощью метода Hide, а с помощью вызова функции Win API ShowWindow(Handle, SW_Hide), не возникнет событие OnHide, потому что оно запускается теми самыми внутренними механизмами Delphi. Но такие недоразумения случаются обычно только тогда, когда функциями Win API дублируется то, что можно сделать и с помощью Delphi. Для вызова функций Win API объекта, созданного с помощью Delphi, используйте свойство Handle. В нём хранится дескриптор.

В некоторых случаях класс Delphi инкапсулирует несколько объектов Windows. Например, класс TBitmap включает в себя HBitmap и HPalette — картинку и палитру к ней. Соответственно, он хранит два дескриптора — в свойствах Handle и Palette.

Все экземпляры классов, созданные в Delphi, должны удаляться. В некоторых случаях это происходит автоматически, в некоторых программист должен сам позаботиться о «выносе му-сора». Аналогичная ситуация и с объектами, создаваемыми в Win API. Если посмотреть справку по функции, создающей какой-то объект, то там обязательно будет информация о том, какой функцией можно удалить объект и может ли система сделать это автоматически. Во многих случаях совершенно разные объекты могут удаляться одной и той же функцией. Так, функция DeleteObject удаляет косметические карандаши, геометрические карандаши, кисти, шрифты, регионы, растровые изображения и палитры. Обращайте внимание на возможные исключения. Например, регионы не удаляются системой автоматически, однако если вызвать для региона функцию SetWindowRgn, то этот регион переходит в собственность операционной системы. Никакие дальнейшие операции с ним, в том числе и удаление, совершать нельзя.

Формы Delphi и окна Windows
Принято считать, что класс TForm реализует окно. Это не совсем верно, потому что TForm реализует лишь часть тех объектов, которые принято называть окнами. Например, кнопка — это тоже окно, но реализуется она классом TButton.

Каждое окно принадлежит к какому-то оконному классу. Не следует путать оконный класс с классами Delphi. Это некий шаблон, определяющий базовые свойства окна. Каждому такому шаблону присваивается имя, уникальное в его области видимости. Классы делятся на локальные (видимые только в приложении, регистрирующем их) и глобальные (видимые вне прило-жения). В 16-разрядных версиях Windows локальный класс, зарегистрированный приложением, был виден всем другим экземплярам этого же приложения. В 32-разрядных версиях различные экземпляры одного приложения стали более самостоятельными, поэтому каждый экземпляр должен заново регистрировать все свои классы. Перед использованием класс необходимо зарегистрировать ( функция RegisterClassEx). При завершении программы все классы, зарегистрированные в ней, удаляются автоматически, хотя при необходимости их можно удалить и самостоятельно. Отсюда очевидно, что глобальный оконный класс, доступный всем программам, должен быть зарегистрирован динамической библиотекой, постоянно находящейся в памяти. Как это сделать, можно прочитать в Win32 Developer’s Reference, тема WNDCLASS. Если же глобальный класс регистрируется программой обычным образом, то это означает, что он будет доступен не только самой программе, но и всем библиотекам, вызванным ею, но никак не другим программам и не другим экземплярам этой программы. Если наоборот, глобальный класс регистрирует DLL, то он становится доступным всем программам, использующим эту DLL. Но в этом случае класс не удаляется автоматически.

При создании окна обязательно указывать его класс. Данный класс должен быть к этому моменту зарегистрирован. В Delphi имя оконного класса для окон, созданных наследниками TForm, всегда совпадает с именем класса Delphi. Существуют предопределённые классы Windows, которые не надо регистрировать. Это ‘BUTTON’, ‘COMBOBOX’, ‘EDIT’, ‘LISTBOX’, ‘MDICLIENT’, ‘SCROLLBAR’ и ‘STATIC’. Назначение этих классов понятно из их названий (класс ‘STATIC’ реализует статические, то есть не реагирующие на мышь и клавиатуру, но имеющие дескриптор элементы, текстовые или графические). Впрочем, можно определить локальный класс с зарезервированным именем, он перекроет глобальный в пределах приложения.

Кроме имени, класс включает в себя другие параметры, такие как стиль, кисть и т. д. Они подробно перечислены в справке по теме WNDCLASS.

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

Функции, создающие окна, требуют указать дескриптор приложения. В Delphi этот дескриптор хранится стразу в двух переменных — MainInstance модуля System и HInstance модуля SysInit. Оба эти модуля автоматически подключаются к любому модулю, созданному в Delphi, так что можно использовать ту или иную переменную по своему вкусу. Кстати, не следует путать автоматическое подключение этих модулей с автоматической генерацией кода IDE Delphi для под-ключения таких модулей, как Windows, Forms, SysUtils и т. д. В первом случае модули подключаются несмотря на то, что не упомянуты в списке uses. Более того, их упоминание там приведёт к ошибке. Во втором случае эти модули явным образом подключаются, просто Delphi автоматически пишет эту часть программы за программиста. Можно написать модуль или даже целую программу, которые не будут использовать SysUtils, но нельзя написать такие, которые не будут использовать System.

Создание окон через Win API требует кропотливой работы. VCL Delphi справляется с этой задачей замечательно, поэтому создавать окна самостоятельно приходится только тогда, когда использование VCL нежелательно, например, если необходимо написать как можно более компактное приложение. Во всех остальных случаях приходится только слегка подправлять работу VCL. Например, с помощью Win API можно изменить форму окна или убрать из него заголовок, оставив рамку. Подобные действия не требуют от программиста создания нового окна, можно воспользоваться тем, что уже создано VCL.

Другой случай, когда могут понадобиться функции Win API для окон — если приложение должно что-то делать с чужими окнами. Например, хотя бы просто перечислить все окна, открытые в данный момент, как это делает WinSight32. Но в этом случае также не приходится самому создавать окна, работа идёт с уже имеющимися.

Оветуем посетить женский рай

 

1.6 Проeкт — шифровка

До сих пор мы ознакомились с вводом и выводом из консоли, управлением переменными и строками, простой математикой. А теперь самое время написать простую программу, где мы все это используем.
Создадим программу, которая будет спрашивать у пользователя четырехзначное число и шифровать его. Возникает вопрос : как, ведь мы не учили технику шифрования? Верно, но мы уже знаем достаточно, чтобы обойтись и без этого: мы знаем как обращаться со строками и этого хватит для простого примера.
Начнем с того, что спросим у пользователя число. К этому моменту мы должны хорошо знать, как это делать:
Console.Write(«Введите 4-хзначное число для шифровки:»);
string rawNumber = Console.ReadLine();

Почему стока, а не целое? Потому, что нам надо совершить различные операции для шифровки над каждой цифрой, а «нарезать» число удобнее с помощью Substring(), вот так:
int digit1 = Convert.ToInt32(rawNumber.Substring(0,1));
int digit2 = Convert.ToInt32(rawNumber.Substring(1,1));
int digit3 = Convert.ToInt32(rawNumber.Substring(2,1));
int digit4 = Convert.ToInt32(rawNumber.Substring(3,1));
Хорошо, теперь у нас каждая цифра хранится в своей переменной. Что дальше? Ради упражнения, допустим, что мы хотим увеличить каждую цифру на 4. То есть, 4 станет 8, 5 — 9, а 6 — 0. так что , число 1236 «превратится» в 5670. Ну, легче всего это делается с помощью «%» (надеюсь еще помним, что это). Потому, что цифр всего 10 (0 — 9) и нам надо «поправить» каждую на 4, все что надо сделать это :
digit1 = (digit1 + 4) % 10;
digit2 = (digit2 + 4) % 10;
digit3 = (digit3 + 4) % 10;
digit4 = (digit4 + 4) % 10;
К каждой цифре добавляется 4, а «%» отсекает десятки. Значит, если число до операции было 11, то останется 1. От 13 останется 3, а если была девятка, то так и останется 9. Все что мы делаем это: увеличиваем значение цифр на 4, а если значение превышает 9, отсекаем десятки. К сожалению, я не математик, поэтому не судите строго за такое объяснение.
Отлично, каждая цифра зашифрована и сохранена. А как же собрать их обратно в число? Ну, один из вариантов — соединить их в одну строку, а потом эту строку сконвертировать в число:
rawNumber = digit1.ToString() + digit2.ToString() + digit3.ToString() + digit4.ToString();
int convertedNumber = Convert.ToInt32(rawNumber);
Может показаться странным то, что мы вызвали метод ToString() на каждую цифру при «склеивании» числа. Ну, а что же происходит, когда мы просто используем оператор «+» над двумя целыми? Правильно, складываются они. Ведь если наши цифры 1,2,3,4 наше конечное число должно быть 1234… не 10, которое мобы получили при простом складывании.Мы используем ToString() чтобы заставить С# складывать переменные как стоки, а не как числа. Ну, конечно есть и более простой способ «склеивания» цифр в число:
int convertedNumber = (digit1*1000) + (digit2*100) + (digit3*10) + digit4;
Например, наши цифры 1,2,3,4. Мы можем выразить этот конкретный случай более наглядно:
int convertedNumber = (1000) + (200) + (30) + 4;
Да здравствует старая добрая десятичная система исчисления! С шифровкой покончили, теперь будем расшифровывать. Это практически то же самое, только наоборот. В нашем случае с зашифрованными цифрами мы делаем следующее:
digit1 = (digit1 + 6) % 10;
digit2 = (digit2 + 6) % 10;
digit3 = (digit3 + 6) % 10;
digit4 = (digit4 + 6) % 10;
Почему добавляем 6? Просто мы уже добавили 4, и при добавлении еще 6-ти, цифры сделают полный оборот(тот же принцип, когда цифры при шифровке превышали 9). Можно это проверить сделав шифрование а затем расшифровку. Мы получим те же числа, что были в начале.
int decryptedNumber = (digit1*1000) + (digit2*100) + (digit3*10) + digit4;
Console.WriteLine(«Decrypted Value: » + decryptedNumber);
Естественно, эта программа ОЧЕНЬ ограничена. Она расчитана на то, что пользователь ТОЧНО введет 4-значое число. Если введут меньше 4 чисел, программа вызовет ошибку. Если введут не цифры, программа тоже вызовет ошибку. К тому же нет другого пути зашифровать новое число, не перезапустив программу. На конец-то пришло время перейти к более сложным программам, которые будут использовать настоящую логику, протекать по-разному, опираясь на действия пользователя. Все это мы узнаем из следующего раздела. Не выключаем компьютер!
А пока, вот пример кода для шифровальной программы:
using System;

namespace F_ProjectEncrpyt
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
// Запрашиваем число
Console.Write(«Введите 4-хзначное число для шифровки:»);
string rawNumber = Console.ReadLine();
Console.WriteLine(Environment.NewLine + «Изначальное значение: » + rawNumber);

// Сохраняем каждую цифру:
int digit1 = Convert.ToInt32(rawNumber.Substring(0, 1));
int digit2 = Convert.ToInt32(rawNumber.Substring(1, 1));
int digit3 = Convert.ToInt32(rawNumber.Substring(2, 1));
int digit4 = Convert.ToInt32(rawNumber.Substring(3, 1));

// Увеличиваем каждую на 4 (4 становится 8 , 5 становится 9, 6 — 0 и т.д…)
digit1 = (digit1 + 4) % 10;
digit2 = (digit2 + 4) % 10;
digit3 = (digit3 + 4) % 10;
digit4 = (digit4 + 4) % 10;

// Используем цифры для «склеивания» нашего зашифрованного числа
int convertedNumber = (digit1 * 1000) + (digit2 * 100) + (digit3 * 10) + digit4;
Console.WriteLine(«Зашифрованное значение: » + convertedNumber);

// Увеличиваем каждую цифру на 6 (возвращаем ей ее начальное значение)
digit1 = (digit1 + 6) % 10;
digit2 = (digit2 + 6) % 10;
digit3 = (digit3 + 6) % 10;
digit4 = (digit4 + 6) % 10;

// Используем цифры для «склеивания» нашего разшифрованного числа
int decryptedNumber = (digit1 * 1000) + (digit2 * 100) + (digit3 * 10) + digit4;
Console.WriteLine(«Разшифрованное значение: » + decryptedNumber);

// А теперь попросим пользователя нажать «Enter» для выхода из программы
Console.WriteLine(«Программа завершена. Нажмите \»Enter\» для выхода…»);
Console.ReadLine();

}
}
}

 

2.1. А что если..? (операторы if…else в C#)

До сих пор наши программы были линейными. То есть, они делали одно и то же. Единственное разнообразие было лишь тогда, когда пользователь неправильно вводил данные, и программа выдавала ошибку! А что, если мы хотим сделать программу, которая будет реагировать на то или иное обстоятельство? А что, если программа даже будет принимать решения, в зависимости от ввода пользователя? Действительно, » А что, если?»… Рассмотрим оператор «if» во всем его величестве:
Console.Write(«Введите свой возраст: «);
int age = Convert.ToInt32( Console.ReadLine() );

if( age == 16 )
{
Console.WriteLine(«В этом возрасте уже можно водить машину!»);

}
Первые две строки должны быть уже знакомы… мы спрашиваем пользователя его возраст. Следующие 4 строки «составляют» наш оператор. На первой линии, где «if» действительно упомянут, мы пишем наши логические выражения. Если перевести на русский, там написано: «если возраст пользователя 16, то выполнить код между символам «{» и «}»». То есть, если пользователь введет 16 как свой возраст, то получит это сообщение. В любом другом случае, код в «if» операторе будет пропущен.
Конечно, == не единственное сравнение, которое мы можем использовать. Вот более полный список:
== (равен)
< (меньше чем)
> (больше чем)
!= (не равен)
<= (меньше или равен)
>= (больше или равен)
Нужно быть осторожным и не спутать = с ==. Обычное = используется для присвоения значений и, практически, не имеет никакого отношения к «if» оператору. == используется для проверки абсолютного равенства. Еще надо опасаться использования == на типах double и decimal, т.к. редко когда бывает, что их значение совпадает с искомым.
Кстати о различиях, надо запомнить, что «age < 17" и "age <= 16" абсолютно одно и то же. Первое означает "любое число меньше 17", а второе "любое число меньшее или равное 16", а результат этих логических выражений одинаков(оба верны для чисел меньше 17).
Все выражения, которые можно вписать в утверждение оператора «if» сводятся к булевому типу — то есть эти выражения либо верны (true) либо неверны (false). Для этого в С# есть специальный тип данных — «bool»(булевой тип). Познакомимся поближе:
bool userAgeIs16 = age == 16;
if( userAgeIs16 )
{
Console.WriteLine(«В этом возрасте уже можно водить машину!»);
}
Этот код делает то же самое, что и в первом примере, только на этот раз мы передали значени «true/false» через булевую переменную. Надо заметить, что большинство начинающих программистов «зависают» на знаках равенства в «if» операторе. Они настолько привыкают видеть вещи типа «age == 16» в условиях, что для них просто неприемлемо, да и непонятно, видеть там булевую переменную. Не надо думать, что необходимо постоянно создавать лишние переменные, просто это показывает, что все эти «==» и цифры, и вообще, выражения в «if» операторе сводятся к одному булевому «верно-неверно»…
А что, если мы хотим, чтобы «if» проверяло сразу несколько вещей? Например, возраст и IQ? Тогда делаем так:
Console.Write(«Введите свой возраст: «);
int age = Convert.ToInt32( Console.ReadLine() );
Console.Write(«Введите свой IQ: «);
int iq = Convert.ToInt32( Console.ReadLine() );

if( age >= 16 && iq > 75 )
{
Console.WriteLine(«С таким умом и в таком возрасте можно водить машину!»);
}
«&&» , которое означает «И», позволяет нашему условию быть более конкретным. Теперь мы получим сообщение, если будут введен возраст больше или равный 16 И IQ больший или равный 75. Если хотя бы одно из выражений неверно, все условие автоматом считается неверным и код из тела «if» будет пропущен.
В противоположность «&&», «||» означает «ИЛИ». В выражении с «ИЛИ», если хотя бы одно из условий верно, все выражение считается верным. Все как и в русском. Если бы я сказал «чтобы купить водку, надо быть старше 18, или попросить дядю Вову из соседнего подъезда». Сразу понятно, что для покупки водки надо быть совершеннолетним ИЛИ попросить дядю Вову. Не надо просить соседа И быть совершеннолетним одновременно. Нужно только ОДНО из этих условий. Мдя, нехороший был пример… Зато все понятно, не так ли?
Можно и поэкспериментировать, использовать все вперемешку. Например, следующее выражение (хотя и достаточно сложное) абсолютно разрешимо:
if( (age != 5 && age 60 && iq > 90) )
Условие верно, если возраст меньше 50 и не равен 5, ИЛИ если он больше 60 и IQ выше 90. Это, конечно сложно, но зато прямолинейно. В любом выражении, как это && будет выполняться раньше ||. Несмотря на это, мы можем заставить С# сначала выполнять содержимое скобок. А еще можно вставлять скобки для улучшения читаемости кода.
Ну что ж , продолжаем. Стоит заметить, что в тело «if» можно вписывать сколько угодно строк кода. Можно даже объявлять новые переменные…
if( age > 15 )
{
Console.Write(«А теперь введите имя: «);
string name = Console.ReadLine();
Console.WriteLine(name + «, ты можешь водить машину!»);
}
Нужно заметить, что все переменные, объявленные внутри «if» оператора, существуют только в нем. Если мы попытаемся использовать переменную вне фигурных скобок, «{}» оператора «if», то программа не заработает — переменная находится «out of scope», т.е. фактически «вне досягаемости». А что если нам надо вызвать переменную после «if»? В этом случае просто объявляем ее до него, вот так…
string name = «»;
if( age > 15 )
{
Console.Write(«А теперь введите имя: «);
name = Console.ReadLine();
Console.WriteLine(name + «, ты можешь водить машину!»);
}
…вот и все. Мы поговорим о границах видимости переменных позже, при рассмотрении функций, а пока надо запомнить: если надо, чтобы у нас был доступ к переменной по всей программе, не надо объявлять переменные внутри «if» оператора. Кстати, если после оператора условия мы планируем только одну строку кода, то можно «{}» опустить… Можно просто…:
if( age > 15 )
Console.WriteLine(«Это тоже работает!»);
Надо запомнить,что при пренебрежении «{}» только строка следующая непосредственно после «if» считается частью условия. Ну, а сейчас ситуация немного усложнится…
if( age > 15 )
{
Console.WriteLine(«Можно водить машину.»);
}
else
{
Console.WriteLine(«Мал еще!»);
}
Мы не только говорим программе, что делать при определенных условиях, но и что же делать, если эти условия не встречаются! Дополнительный блок «else» в «if» операторе пишется в конце и работает так же. Основное различие в том, что мы не передали условное выражение, потому что «else» «ловит» все те условия, которые не соответствуют основному(которое в «if»). А что если нам необходимо иметь несколько определенных путей развития? Можно сделать так:
if( age == 16 )
{
Console.WriteLine(«Можно водить машину.»);
}
else if( age > 15 )
{
Console.WriteLine(«Едва ли может водить!»);
}
else
{
Console.WriteLine(«Мал еще!»);
}
Можно добавлять сколько угодно «else if» блоков к «if» оператору, чтобы предусмотреть различные ситуации. Как «else if», так и «else» блок является необязательным для «if» оператора; как бы то ни было, если «else» все же используется, он должен быть в самом конце. Еще надо заметить и объяснить почему мы написали условие age > 15 в блоке «else if». Дело в том, что в любой if/else if/else структуре, С# выполняет только первое попавшееся совпадение. Значит, если age >15 стоял бы в начале, и пользователь вписал бы 16 как свой возраст, age >15 было бы первым встречным совпадением. Выполнится «Можно водить машину.» код, вместо более конкретного «Едва ли может водить!», т.е. вместо того, который нам нужен. Нужно быть осторожным при написании условий, приходится следить за тем, чтобы первые выражения не «крали» условия у других.
Ну чтож, мы изрядно потрудились, но еще не все кончено! Прежде чем продолжить, рассмотрим пример кода…
using System;

namespace A_If
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
// Запрашиваем возраст
Console.Write(«Введите свой возраст: «);
int age = Convert.ToInt32(Console.ReadLine());

// Простой if/else if/else
if (age == 16)
{
Console.WriteLine(«В этом возрасте уже можно водить машину!»);
}
else if (age < 16)
{
Console.WriteLine(«Слишко мал для вождения!»);
}
else if (age > 16 && age < 150)
{
Console.WriteLine(«Уже можно было бы и получить права!»);
}
else
{
Console.WriteLine(«Ну да, конечно…»);
}

// Одностроковый if оператор
if (age < 0)
Console.WriteLine(«Минуточку, возраст не может быть отрицательным!»);

// Булевые переменные — то «горючее», на котором «работают» if операторы
bool oldEnough = age >= 18;
if (oldEnough)
Console.WriteLine(Environment.NewLine + «Можно голосовать на выборах.»);
else
Console.WriteLine(Environment.NewLine + «Слишком молод для голосования.»);

// «or» логика
Console.Write(Environment.NewLine + «Введите число делимое либо на 4, ЛИБО на 5: «);
int num = Convert.ToInt32(Console.ReadLine());
if ((num % 4 == 0) || (num % 5 == 0))
Console.WriteLine(«Правильно.»);
else
Console.WriteLine(«Неправильно.»);

// «and» логика
Console.Write(Environment.NewLine + «Введите число, делимое на 4 И на 5: «);
num = Convert.ToInt32(Console.ReadLine());
if ((num % 4 == 0) && (num % 5 == 0))
Console.WriteLine(«Правильно.»);
else
Console.WriteLine(«Неправильно.»);

// Ждем выхода…
Console.Write(Environment.NewLine + «Программа завершена. Нажмите \»Enter\» для выхода…»);
Console.ReadLine();
}
}
}

 

1.2 Переменные в C# (Csharp)

Ну что ж, знать WriteLine() — очень хорошо, но если мы хотим сделать что-нибудь дельное, нам надо познакомиться с переменными. Из-за того, что они очень важны в работе любой программы, нам придется изрядно потрудиться. Но не надо вешать нос! То что мы выучим сегодня точно также работает и в Java, и в C++. Так что мы изучим такое, что приготся и в других языках!

Переменная работает как хранилище, сосуд для информации. Зачем нам это надо? В общем, для гибкости. Мы можем вложить в нее любое значение, а потом использовать и изменять как душе угодно, да и когда душе угодно. Зачем писать код десятки раз, когда можно сделать это однажды и просто использовать одну переменную с нужными нам данными? Переменные особенно важны когда имеем дело с вводом данных извне и понадобится где-то хранить информацию, которую предоставил пользователь. Прежде чем войти в эту быструю реку, познакомимся с переменными поближе, узнаем как они работают. Смотрим:
string programmerName = «Dr.Acid»;
Console.WriteLine(«Эту статью написал » + programmerName + «.»);
Мдааа… Достаточно загадочно , нет? Как мы наверно уже догадались «string» («строка») — это просто термин, который подразумевает текст («строка» букв,если угодно). Код свыше должен выдать такой результат:
Эту статью написал Dr.Acid.

Теперь, когда мы знаем с чем имеем дело, давайте внимательнее посмотрим на строку, где мы «объявили» переменную (кстати, говорю это впервые). Первое слово в строке, «string», говорит С# что мы создаем переменную строкового типа (да, есть переменные и другого, но это позже, а сейчас СКОНЦЕНТРИРУЙСЯ!) Второе слово, «programmerName»(» имяПрограммиста» в переводе) — имя нашей переменной. Имена могут быть разнообразной последовательностью букв,чисел и подчеркиваний. Если имя не начинается с цифры, или содержит знаки пунктуации кроме подчеркивания, это правильные имена. Очевидно, что необходимо сделать имя переменой как можно более полезной, например своим именем указать на содержание. То есть в данном случае «programmerName» — отличное имя для переменной, а «engineOil»(«Машинное масло» в переводе) — нет.

Знак «=» говорит С# что мы хотим присвоить нашей переменной значение, а текст в кавычках — само значение. Теперь С# знает что надо вставить слово «Dr.Acid» везде где используется переменная programmerName. Изменение значения переменной не сложнее изменения текста в кавычках.

Еще мы можем копировать значение переменной, сделав ее «равной» другой переменной того же типа. Например:
string coolProgrammer = programmerName;
«programmerName» уже имеет значение — «Dr.Acid». Как только этот код выполнится, «coolProgrammer» («крутойПрограммист») тоже будет иметь значение «Dr.Acid». Мы в любое время можем изменить значение переменной, используя оператор присваивания «=» …
coolProgrammer = » Ты»;
Теперь «coolProgrammer» имеет значение «Ты» (Знаю,знаю подхалимство ничего хорошего не даст). Заметим, что мы не использовали ключевое слово «String» в начале строки. Это потому что мы уже «объявили» coolProgrammer как переменную строкового типа, немного раньше. Нужно указывать тип переменной в самый первый раз, когда упоминаем о ней, а если попытаемся объявить ее по новой, С# злостно накричит на нас. Заметим, что совсем не обязательно сразу же давать переменной значение. Следующий пример абсолютно легален:
string profession;
profession = «плотник»;
Просто надо запомнить что мы не имеем права использовать переменные где-либо в коде до тех пор, пока не дадим им значение. Ну что ж, хватит наверно про строки. Давайте сохранять числа! Смотрим:
int headCount = 29;
Console.WriteLine(«В аудитории » + headCount + » человек.»);
«int» — это целое число, безо всяких десятичных дробей. Они идеальны в ситуациях, где дробные числа не нужны(вести подсчет людей в дробях довольно неудобно, да и кто хочет кругом были их части?) Как видно, выводить их в консоль можно также как и переменные строкового типа.

Можно даже конвертировать строки в целые, но нужно быть осторожным! Операция сработает только если ВСЯ строка представляет из себя целое. Например, «10»,»-412″,и «38899» все без проблем сконвертируются в целые. А вот «мяу», «-34-«, и даже «100 мяу» — нет. А если операция конверсии провалится, в программе возникнет фатальная ошибка и она вылетит: без извинений, без предупреждений, как снег на голову… Позже мы научимся справляться и с этим, а пока нужно просто быть осторожным с конвертированием строк в целые. Вот как мы это делаем:
string someNumberText = «123»;
int convertedNumber = Convert.ToInt32(someNumberText);
Console.WriteLine(«Я получил число » + convertedNumber + » из строки!»);
Также нам ничего не стоит сконвертировать переменную любого типа в строоковый, используем метод ToString() . Смотрим:
string convertedString = convertedNumber.ToString();
Console.WriteLine(«Я получил эту строку из переменной целого типа: » + convertedString);
Фактически каждый тип имеет метод ToString(), который значительно облегчает конвертацию чисел в строки. Пока что это нам может и не понадобится, но запомнить это надо. Хорошо запомнить! Конечно, не каждое число можно представить как гладковыбритое целое. Часто нам нужно использовать дробные числа, знакомтесь :
double height = 35.5;
Console.WriteLine(«Высота равна: » + height);
Мы используем тип «double»(«двойной») чтобы иметь под рукой дроби.(Изначально это назвали «double» потому, что памяти занимает вдвое больше, чем «int». Все просто!) Ах, да, есть еще и метод Convert.ToDouble() , для того чтобы получать дробные числа из строк. Работает так же как и для целых. Кстати, а что если нам надо присвоить целое значение в переменную типа «double»? Ну, грубо говоря, «int» — это «double», содержащий меньше информации… Так что это не сложнее чем:
int integerHeight = 40;
height = integerHeight;
Console.WriteLine(«Теперь высота равна: » + height);
Как видим, можно без проблем «вставлять» целые значения в двойные. А вот присваивание значения double переменной целого типа не так-то и просто. Так как INTы содержат только целые значения, любая дробная информация двойной переменной будет утеряна при присваивании ее INTу. С# обычно не «любит» делать этого, поэтому мы должны указать программе, что все в порядке. Заметим, что С# не позаботится о том, чтобы округлить нашу дробь до близжайшего целого (если не заставим, а об этом позже). Просто все дробные значения будут «выброшены» из числа.
height = 32.953;
integerHeight = (int)height;
Console.WriteLine(«Целая height теперь равна: » + integerHeight);
(int) просто говорит С# :»Говорю тебе — это целая!» Существует две разновидности double — «float» и «decimal». «float»ы менее точные, а «decimal»ы невероятно точные. Нужно использовать явные преобразования ( (int) в нашем примере) для того чтобы передавать значения между переменными этих типов: double, float и decimal. Чаще всего можно увидеть «float»ы в изобразительных командах(координаты и т.д.), тогда как «decimal»ы используются там, где требуется абсолютная точность, например действия с деньгами. Не надо внушать себе что надо везде использовать «decimal»ы из-за их точности. Скорость и эффективность — важнейшие элемты программы, поэтому не надо содержать информацию в числе, где после запятой 100,000 цифр, когда можно обойтись и 1,000. Фактически, мы не будем иметь дело с «float» и «decimal» довольно долго.
А теперь позабавимся. Следующий код прикольно читать?
Console.WriteLine(«Имя — » + programmerName + «, высота — » + height + «, кол-во — » + headCount);
Если ответ «да», то это вранье. На самом деле эта строка достаточно сложна. Нужно разбираться, что там такое творится между всеми этими плюсами. Разве не было бы учше иметь целое, «ненарезанное» сообщение и отбросить всю возню с переменными в конец? Может даже вот так?
Console.WriteLine(«Имя — {0}, высота — {1}, кол-во — {2}», programmerName, height, headCount);
Этот код даст тот же резултат, что и предидущий, но просто мы откладываем указание переменных на конец. Все что нужно сделать — это вставить указатели на переменные там, где нам нужно их видеть, а потом, в конце, прописать все переменные в правильном порядке. {0} указаывает на первую переменную из списка,который мы указали в конце, {1} — вторую и т.д.(отсчет начинается с 0). А количество вот таких «вставок» практически бесконечно!
Ну ладно, еще пару трюков и на сегодня хватит. Можно одновременно объявить сразу несколько переменных. Вот так:
int a, b, c, d;
…а также можно присвоить одно значение сразу нескольким переменным. Смотрим:
a = b = c = d = 100;
Ну вот и все… Конечно, это все очень хорошо — переменные всякие… Но программа будет выдавать один и тот же результат при каждом запуске, до тех пор, пока программист не изменит начальный код. Время учиться обрабатывать данные, введенные пользователем… Но сначала пример кода:
using System;

namespace B_Variables
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
// объявляем строковую переменную, присваиваем ей значение
string programmerName = «Dr.Acid»;
Console.WriteLine(«Эту статью написал » + programmerName + «.»);

// копируем значение одной переменной в другую
string coolProgrammer = programmerName;
Console.WriteLine(coolProgrammer + » is a cool programmer!»);

// Объявляем целое
int headCount = 29;
Console.WriteLine(«В аудитории » + headCount + » человек.»);

// переводим строку в число
string someNumberText = «123»;
int convertedNumber = Convert.ToInt32(someNumberText);
Console.WriteLine(«Я получил число » + convertedNumber + » из строки!»);

// переводим число в строку
string convertedString = convertedNumber.ToString();
Console.WriteLine(«Я получил эту строку из переменной целого типа: » + convertedString);

// объявляем дробную переменную
double height = 25.8;
Console.WriteLine(«Высота равна: » + height);

// еще можем в любое время изменить значение переменной
height = 58.32;
Console.WriteLine(«Теперь высота равна: » + height);

// придаем дробной переменной целое значение
int integerHeight = 40;
height = integerHeight;
Console.WriteLine(«Теперь высота равна: » + height);

// придаем целой переменной дробное значение (указываем)
height = 32.953;
integerHeight = (int)height;
Console.WriteLine(«Теперь высота равна (целое) : » + integerHeight);

// можно использовать сколько угодно переменных при выводе информации

// Уродливый вид
Console.WriteLine(«Имя — » + programmerName + «, высота — » + height + «, кол-во — » + headCount);

// Красивый вид
Console.WriteLine(«Имя — {0}, высота — {1}, кол-во — {2}», programmerName, height, headCount);

// объявляем несколько переменных
int a, b, c, d;

// присваиваем значение нескольким переменным
a = b = c = d = 100;
Console.WriteLine(«a={0}, b={1}, c={2}, d={3}», a, b, c, d);

// Заставим программу подождать пока пользователь не нажмет «Enter»
Console.ReadLine();
}
}
}

 

1.1 Вывод в консоль с использованием C# (Csharp)

Сразу возьмёмся за дело и сделаем первый шаг в мир С# (Csharp)… А начнём мы с консольной программы:
using System;

namespace A_ConsoleOutput
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine(«Здравствуй, Мир!»);
}
}
}
Всё что делает эта программа — показывает «Здравствуй, Мир!» в маленьком оконце консоли. Может показаться, что для такой простой задачи такое количество кода огромно, но есть и хорошие новости — пока не придется беспокоиться об этом. Скоро мы будем полностью понимать его значение, но пока заострим внимание на конкретной строке:

Console.WriteLine(«Здравствуй, Мир!»);
Это единственная настоящая команда за всю программу. Мы используем WriteLine() где нам нужно для того чтобы вывести текст в окно консоли. Обратим внимание на следующие 3 строки кода:
Console.WriteLine(«Один…»);
Console.WriteLine(«Два…»);
Console.WriteLine(«Три…»);
В консоли мы увидим такой результат:
Один…
Два…
Три…
Всё просто. У WriteLine() есть родственница Write():
Console.Write(«Четыре…»);
Console.Write(«Пять…»);
Console.Write(«Шесть…»);
Console.WriteLine(«А теперь выведем текст на в новой строке!»);
В консоли мы увидим такой результат:
Четыре…Пять…Шесть…А теперь выведем текст на в новой строке!
Как видно, разница между WriteLine() и Write() довольно очевидна. Когда вызываешь WriteLine(), текст автоматически выведется с новой строки. А когда используешь Write() — текст добавляется к данной строке. Ну что ж, пожалуй про вывод текста в консоль всё… Прежде чем закончить, взглянем ещё на пару вещей.
Если надо получить пустую строку, можно сделать так:
Console.WriteLine(«»);
Ещё можно использовать Environment.NewLine чтобы вставить сколько угодно пустых строк. Надо использовать оператор «+» между каждым из «звеньев» цепи вывода:
Console.WriteLine(«Новая строка…» + Environment.NewLine + «… теперь ДА!»);
Будет такой результат:
Новая строка…
… теперь ДА!!
Можешь вставлять в текст TABы , вкладки (или просто куча пробелов… ) , вставляя «\t» в свой текст. Вот так:
Console.WriteLine(«Вкладка\t Вкладка2\t Вкладка3»);
Код, приведенный выше, приведет к тому, что в консоли выведутся слова «Вкладка Вкладка2 Вкладка3»,разделенные большим расстоянием (вкладкой). А что если мне надо использовать кавычки в тексте? В С# кавычки играют особую роль, т.к. они используются для указания данных в виде текста(в надписи «Здравствуй, Мир!» например). А если просто вставить кавычки в текст и посмотреть что будет? Смотрим :
Console.WriteLine(«Когда-нибудь читал «Войну и Мир»?»);
… С# страшно сконфузится, думая что мы хотим закончить наш текст до слова «Война». Мы пойдем другим путем — просто вместо знака » используем \» , что скажет С# о том что мы хотим не прервать текст, а вставить старые добрые кавычки… Смотрим :
Console.WriteLine(«Когда-нибудь читал \»Войну и Мир\»?»);
Что нам и даст:
Когда-нибудь читал «Войну и Мир»?
Как видно, в обоих случаях, как с кавычками, так и с вставками, нужна обратная косая черта «\» для обозначения спец. символов. Рождается вопрос: а если нужна сама обратная косая «\» ? Все просто:
Console.WriteLine(«Это обратная косая черта: \\»);
Знак «\\» переводится в «\». Некоторые программисты говорят, что удобно писать с новой строки при помощи «\n» , вместо использования Environment.NewLine . Да, это работает, но НЕ ВСЕГДА, особенно если работаешь с Windows Forms. Если придерживаться Environment.NewLine , точно будешь знать, что начнёшь с новой строки. Каждый раз.
Еще одна интересная вещь с «\» — можно вставлять специальные символы. Например «263B». Можно использовать «\u» с кодом спец. символа для вывода этого символа (в данном случае этот символ — смайлик):
Console.WriteLine(«\u263B»);
Теперь мы знаем почти всё что нужно для понимания кода в конце статьи. Осталась одно — когда консольная программа выполняет исполняемый код, то она умирает… (заканчивает работу, закрывается, называй как хочешь…). То есть программа пронесётся сквозь строки WriteLine() и Write() и тут же закроет окно прежде чем увидим результат. Поэтому добавим одну строку в конец кода :
Console.ReadLine();
Это заставляет ждать программу пока пользователь не нажмет «Enter». ReadLine() также имеет огромнейшее значение, но нам знать об этом пока довольно рано.
Ах да ! Еще кое-что : если ввести «//» в любом месте кода, С# будет просто игнорировать остальную часть строки. Это отлично подходит для добавления «комментариев» к коду. Например:
// Поздоровайся с Миром!
Console.WriteLine(«Здравствуй, Мир!»);
После «//» (кстати, называется «comment» — «комментарий») можно писать все что угодно. Это очень удобно оставлять заметки в коде, так что ОБЯЗАТЕЛЬНО используйте эту возможность! Пока это может показаться нелепым , но программисты часто забывают то КАК работает их код. Поверь, это когда-нибудь СЛУЧИТСЯ, и комментарии спасут тебе жизнь!(Ну, если быть точным — кучу времени, ведь разбирать и понимать забытый довольно долго…) Так что НЕ игнорируй их!
Ну теперь точно всё! Сейчас этого достаточно для того чтобы понять первый образец кода. В основном сконцентрируй внимание на код в середине программы. Пока не обращай внимание на { } , «static void main» , «class» , «namespace» и остальную чушь — мы вернемся к ним позже.
using System;

namespace A_ConsoleOutput
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
// *** Сама программа начинается здесь ***

// «//» указывает на то, что вся остальная часть линии — комментарий
// В комментарии можно писать все что угодно.

// Вывести текст на консоль
Console.WriteLine(«Здравствуй, Мир!»);

// WriteLine() означает что вводимый текст выведется в новой строке

Console.WriteLine(«Один…»);
Console.WriteLine(«Два…»);
Console.WriteLine(«Три…»);

// Write() означает что вводимый текст выведется в той же строке

Console.Write(«Четыре…»);
Console.Write(«Пять…»);
Console.Write(«Шесть…»);
Console.WriteLine(«А теперь выведем текст на в новой строке!»);

// А это просто вставляет пустую строку
Console.WriteLine(«»);

// Environment.NewLine может вставить пустую строку в середине текста
// Используй оператор «+» между каждой частью текста
Console.WriteLine(«Новая строка…» + Environment.NewLine + «… теперь ДА!»);

// Можно использовать «\t» для создания вкладок в текст
Console.WriteLine(«Вкладка\t Вкладка2\t Вкладка3»);

// «\\» вставляет обратную косую, а » \» » вставляет кавычки
Console.WriteLine(«Обратная косая:\\ Кавычка:\»»);

// Заставим программу подождать пока пользователь не нажмет «Enter»
Console.ReadLine();

// *** Программа заканчивается здесь ***
}
}
}

 

1.3 Ввод данных в консоль C# (Csharp)

После того долгого знакомства с переменными, этот урок может показаться быстрым и легким. Ну, в общем, ReadLine() — это еще один способ присвоить переменной значение. Смотрим:
Console.Write(«Введите имя и нажмите Enter: «);
string name = Console.ReadLine();
Console.WriteLine(«Привет, » + name + «!»)
Фух! Миссия завершена! За три строки кода, мы запросили ввод, сохранили его и использовали. Вовсе не обязательно писать метод Write() перед ReadLine(), просто обычно надо каким-либо образом сказать пользователю, какой тип ввода мы ожидаем. Это просто вежливо.
Наверное, единственное затруднение — это то, что ReadLine() всегда возвращает данные строкового типа. Чтобы получить числа, надо опять же использовать методы класса Convert:
Console.Write(«Введите свой возраст и нажмите Enter: «);
int age = Convert.ToInt32( Console.ReadLine() );
Console.Write(«Введите свой рост и нажмите Enter (можно использовать дроби):»);
double height = Convert.ToDouble( Console.ReadLine() );
Console.WriteLine(«Значицца, вам {0} лет, а рост {1}.», age, height);

Надо запомнить, что без подобающего управления ошибками, программа будет выдавать ошибку каждый раз, когда мы будем пытаться конвертировать «пингвин»-а в число.
А что же с ReadLine() , который мы до сих пор ставили в конце каждой программы? Мы просто использовали преимущества системы ввода С#. Когда мы использовали ReadLine() в конце программы, мы заставляли С# терпеливо ждать, пока пользователь завершит ввод и нажмет ENTER. Когда ENTER нажат, программа собирает всю информацию, что ввел пользователь и … ничего с ней не делает, потому что мы не присваиваем значение никакой переменной. Поэтому С# остается только пожать плечами, забыть все, что ввел пользователь и продолжить работу. Ну разве не грех не использовать такую возможность?

В любом случае, ввод пользователя один из самых коварных аспектов программирования. Невозможно перечислить количество случаев, когда неожиданный ввод может привести программу к внезапному краху! Позже мы узнаем, как же все-таки управляться с глупостью некоторых пользователей, а пока надо запомнить: любая часть кода, которая зависит от внешних данных, будь то ввод с консоли, текстовый файл, или сетевой накопитель — это потенциальная опасность краха и с ней надо обращаться с должной осторожностью. Ну, что ж, самое время для примера кода:
using System;

namespace C_ConsoleInput
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
// Запросить имя пользователя
Console.Write(«Введите имя и нажмите Enter: «);

// Прочесть данные; присвоить значение переменной «name»
string name = Console.ReadLine();

// «name» можно использовать как обычную переменную
Console.WriteLine(«Hello, » + name + «!»);

// Сделаем это снова, только теперь запросим тип пиццы
Console.Write(«Введите название пиццы и нажмите Enter: «);
string pizza = Console.ReadLine();

// А теперь используем обе переменные
Console.WriteLine(«Привет, {0}! Одна {1} пицца на подходе!», name, pizza);

// Используем ReadLine() с численными типами
Console.Write(«Введите свой возраст и нажмите Enter: «);
int age = Convert.ToInt32( Console.ReadLine() );
Console.Write(«Введите свой рост и нажмите Enter (можно использовать дроби): «);
double height = Convert.ToDouble( Console.ReadLine() );
Console.WriteLine(«Значицца, вам {0} лет, а рост {1}.», age, height);

// А теперь попросим пользователя нажать «Enter» для выхода из программы
Console.Write(«Программа завершена. Нажмите \»Enter\» для выхода…»);
Console.ReadLine();
}
}
}