Программа – правильно построенная (не вызывающая возражений со стороны C#-компилятора) последовательность предложений, на основе которой формируется сборка.
В общем случае, программист создает файл, содержащий объявления классов, который подается на вход компилятору. Результат компиляции представляется транслятором в виде сборки. В принципе сборка может быть двух видов:
В любом случае на основе входного кода транслятор строит модуль на IL, манифест, и формирует сборку. В дальнейшем сборка либо может быть выполнена после JIT-компиляции, либо может быть использована в составе других программ.
.NET Framework располагает большим набором полезных функций. Каждая из них является членом какого-либо класса. Классы группируются по пространствам имен. Это означает, что в общем случае имя класса может иметь сложную структуру — состоять из последовательности имен, разделенных между собой точками. Последнее имя в этой последовательности собственно и является именем класса. Классы, имена которых различаются лишь последними членами (собственно именами классов) последовательностей, считаются принадлежащими одному пространству имен.
Средством "навигации" по пространствам имен, а точнее, средством, которое позволяет сокращать имена классов, является оператор
using <ИмяПространстваИмен>;
В приложении может объявляться собственное пространство имен, а также могут использоваться ранее объявленные пространства.
В процессе построения сборки транслятор должен знать расположение сборок с заявленными для использования пространствами имен. Расположение части сборок известно изначально. Расположение всех остальных требуемых сборок указывается явно (непосредственно в Visual Studio при работе над проектом открыть окно Solution Explorer, выбрать пункт References, далее Add Reference... – там надо задать или выбрать соответствующий .DLL- или .EXE-файл).
В частности, сборка, которая содержит классы, сгруппированные в пространстве имен System, располагается в файле mscorlib.dll.
Наиболее часто используемое пространство имен – System. Расположение соответствующей сборки известно. Если не использовать оператор
using System;
корректное обращение к функции WriteLine(...) – члену класса Console выглядело бы следующим образом:
System.Console.WriteLine("Привет!"); // Полное квалифицированное //имя функции – члена класса Console, отвечающей за вывод строки в окно приложения.
При компиляции модуля транслятор по полному имени функции (если используется оператор using – то по восстановленному на его основе) находит ее код, который и используется при выполнении сборки.
Классы и структуры являются программно определяемыми типами, которые позволяют определять (создавать) новые типы, специально приспособленные для решения конкретных задач. В рамках объявления класса и структуры описывается множество переменных различных типов (набор данных — членов класса), правила порождения объектов — представителей структур и классов, их основные свойства и методы.
В программе класс объявляется с помощью специальной синтаксической конструкции, которая называется объявлением класса. Фактически, объявление структур и классов является основным элементом любой C# программы. В программе нет ничего, кроме объявлений и конструкций, облегчающих процедуру объявления.
С точки зрения синтаксиса, между объявлениями классов и структур существуют незначительные различия (ключевые слова struct и class, в структуре не допускается объявлений членов класса со спецификаторами доступа protected и protected internal, при объявлении структуры не допускается объявление конструктора без параметров), часть из которых будет рассмотрены далее.
Основное их различие состоит в том, что класс и структура принадлежат к двум различным категориям типов – типов-ссылок и типов-значений.
В этом разделе обсуждаются основные правила объявления классов.
Объявление класса состоит из нескольких элементов:
Атрибуты – средство добавления декларативной (вспомогательной) информации к элементам программного кода. Назначение атрибутов: организация взаимодействия между программными модулями, дополнительная информация об условиях выполнения кода, управление сериализацией (правила сохранения информации), отладка и многое другое.
Модификаторы прав доступа обеспечивают реализацию принципа инкапсуляции, используются при объявлении классов, структур и их составляющих компонентов. Представлены следующими значениями:
public | Обозначение для общедоступных членов класса. К ним можно обратиться из любого метода любого класса программы |
protected | Обозначение для членов класса, доступных в рамках объявляемого класса и из методов производных классов |
internal | Обозначение для членов класса, которые доступны из методов классов, объявляемых в рамках сборки, где содержится объявление данного класса |
protected internal | Обозначение для членов класса, доступных в рамках объявляемого класса, из методов производных классов, а также доступных из методов классов, которые объявлены в рамках сборки, содержащей объявление данного класса |
private | Обозначение для членов класса, доступных в рамках объявляемого класса |
Спецификатор разделения объявления класса partial позволяет разбивать код объявления класса на несколько частей, каждая из которых размещается в собственном файле. Если объявление класса занимает большое количество строк, его размещение по нескольким файлам может существенно облегчить работу над программным кодом, его документирование и модификацию. Транслятор способен восстановить полное объявление класса. Спецификатор partial может быть использован при объявлении классов, структур и интерфейсов.
Сочетание ключевого слова class (struct, interface) и имени объявляемого класса (структуры или интерфейса) задает имя типа.
Конструкции
:имя класса (при объявлении класса) :список имен интерфейсов (при объявлении структуры или класса) :имя класса, список имен интерфейсов (при объявлении класса)
с обязательным разделителем ':' обеспечивают реализацию принципа наследования.
Тело класса в объявлении ограничивается парой разделителей '{', '}', между которыми располагаются объявления данных — членов и методов класса.
Следующий пример демонстрирует использование основных элементов объявления структуры. При объявлении класса допускается лишь один явный спецификатор – public (здесь он опущен). Отсутствие спецификаторов доступа в объявлениях членов структуры (класса) эквивалентно явному указанию спецификаторов private.
// Указание на используемые пространства имен. using System; using System.Drawing; namespace qwe // Объявление собственного пространства имен. Начало. { // Начало объявления структуры struct S1 {// Тело структуры – НАЧАЛО // Объявление данных-членов. private Point p; // protected int qwe; // Спецификатор protected в объявлении членов // структуры недопустим. Структура не имеет развитого механизма //наследования. // Структура не может иметь конструктора без параметров. public S1(int x, int y) { p = new Point(10,10); } // Объявление методов. // Статический метод. Точка входа. static void Main(string[] args) { // Тело метода. Здесь обычно располагается программный код, // определяющий функциональность класса. } }// Тело структуры – КОНЕЦ } // Объявление собственного пространства имен. Конец.
Все встроенные типы C# однозначно отображаются, а фактически совпадают с системными типами каркаса Net Framework, размещенными в пространстве имен System. Поэтому всюду, где можно использовать имя типа, например, int, с тем же успехом можно использовать и имя System.Int32.
Встроенные типы языка C# и их основные характеристики
Тип | Размер в байтах | Тип .NET | Описание |
---|---|---|---|
Базовый тип | |||
object | Object | Может хранить все что угодно, т.к. является всеобщим предком | |
Логический тип | |||
bool | 1 | Boolean | true или false |
Целые типы | |||
sbyte | 1 | SByte | Целое со знаком (от -128 до 127) |
byte | 1 | Byte | Целое без знака (от 0 до 255) |
short | 2 | Int16 | Целое со знаком (от -32768 до 32767) |
ushort | 2 | UInt16 | Целое без знака (от 0 до 65535) |
int | 4 | Int32 | Целое со знаком (от -2147483648 до 2147483647) |
uint | 4 | UInt | Целое число без знака ( от 0 до 4 294 967 295) |
long | 8 | Int64 | Целое со знаком (от -9223372036854775808 до 9223372036854775807) |
ulong | 8 | UInt64 | Целое без знака (от 0 до 0fffffffffffffff) |
Вещественные типы | |||
float | 4 | Single | Число с плавающей точкой двойной точности. Содержит значения приблизительно от -1.5*10-45 до +3.4*1038 c 7 значащими цифрами |
double | 8 | Double | Число с плавающей точкой двойной точности. Содержит значения приблизительно от -5. 0*10-324 до -1.7*10308 c 15-16 значащими цифрами |
Символьный тип | |||
char | 2 | Сhar | Символы Unicode |
Строковый тип | |||
string | String | Строка из Unicode-символов | |
Финансовый тип | |||
decimal | 12 | Decimal | Число до 28 знаков с фиксированным положением десятичной точки. Обычно используется в финансовых расчетах. Требует суффикса <<m>> или <<М>> |
Переменные и типы - тесно связанные понятия. С объектной точки зрения переменная - это экземпляр типа. Скалярную переменную можно рассматривать как сущность, обладающую именем, значением и типом. Имя и тип задаются при объявлении переменной и остаются неизменными на все время ее жизни. Значение переменной может меняться в ходе вычислений, эта возможность вариации значений и дало имя понятию переменная. Получение начального значения переменной называется ее инициализацией. Важной новинкой языка C# является требование обязательной инициализации переменной до начала ее использования. Попытка использовать неинициализированную переменную приводит к ошибкам, обнаруживаемым еще на этапе компиляции. Инициализация переменных, как правило, выполняется в момент объявления, хотя и может быть отложена.
При объявлении простых переменных указывается их тип и список имен, возможно, с инициализацией, например:
int x = 1, y = 0;
Константы C# могут появляться, как обычно, в виде литералов и именованных констант. Вот пример константы, заданной литералом и стоящей в правой части оператора присваивания:
y = 7.7f;
Значение константы "7.7f" является одновременно ее именем, оно же позволяет однозначно определить тип константы. Иногда, как в данном случае, приходится добавлять к значению специальные символы для точного указания типа.
Всюду, где можно объявить переменную, можно объявить и именованную константу. Синтаксис объявления схож. В объявление добавляется модификатор const, инициализация констант обязательна и не может быть отложена. Инициализирующее выражение может быть сложным, важно, чтобы оно было вычислимым в момент его определения. Вот пример объявления констант:
const int SmallSize = 38, LargeSize =58;
const int MidSize = (SmallSize + LargeSize)/2;
const double pi = 3.141593;
X = expr;
Здесь переменной X присваевается значение выражения expr. Допускается одновременное присваивание со списком переменных в левой части:
X1 = X2 = ... = Xk = expr;
С помощью фигурных скобок несколько операторов языка (возможно, перемежаемых объявлениями) можно объединить в единую синтаксическую конструкцию, называемую блоком или составным оператором
{ оператор_1 ... оператор_N }
В приведенной выше записи блока, следуя синтаксису C#, каждый из операторов заканчивается символом "точка с запятой". Но, заметьте, блок не заканчивается этим символом!
Синтаксически блок воспринимается как единичный оператор и может использоваться всюду в конструкциях, где синтаксис требует одного оператора. Тело цикла, ветви оператора if, как правило, представляются блоком.
Пустой оператор - это "пусто", завершаемое точкой с запятой. Иногда полезно рассматривать отсутствие операторов как существующий пустой оператор. Синтаксически допустимо ставить лишние точки с запятой, полагая, что вставляются пустые операторы. Например, синтаксически допустима следующая конструкция:
for (int j=1; j<5; j++) {;;;};
Она может рассматриваться как задержка по времени, работа на холостом ходе.
В языке C# для выбора одной из нескольких возможностей используются две конструкции - if и switch. Первую из них обычно называют альтернативным выбором, вторую - разбором случаев.
Синтаксис оператора if
if(выражение_1) оператор_1 else if(выражение_2) оператор_2 ... else if(выражение_K) оператор_K else оператор_N
Выражения if должны заключаться в круглые скобки и быть булевого типа. Точнее, выражения должны давать значения true или false. Каждый из операторов может быть блоком - в частности, if-оператором. Поэтому возможна и такая конструкция:
if(выражение1) if(выражение2) if(выражение3) ...
Ветви else и if, позволяющие организовать выбор из многих возможностей, могут отсутствовать. Может быть опущена и заключительная else-ветвь. В этом случае краткая форма оператора if задает альтернативный выбор - делать или не делать - выполнять или не выполнять then-оператор.
Семантика оператора if проста и понятна. Выражения if проверяются в порядке их написания. Как только получено значение true, проверка прекращается и выполняется оператор (это может быть блок), который следует за выражением, получившим значение true. С завершением этого оператора завершается и оператор if. Ветвь else, если она есть, относится к ближайшему открытому if.
Частным, но важным случаем выбора из нескольких вариантов является ситуация, при которой выбор варианта определяется значениями некоторого выражения. Соответствующий оператор C# называется оператором switch. Вот его синтаксис:
switch(выражение) { case константное_выражение_1: [операторы_1 оператор_перехода_1] ... case константное_выражение_K: [операторы_K оператор_перехода_K] [default: операторы_N оператор_перехода_N] }
Ветвь default может отсутствовать. Заметьте, по синтаксису допустимо, чтобы после двоеточия следовала пустая последовательность операторов, а не последовательность, заканчивающаяся оператором перехода. Константные выражения в case должны иметь тот же тип, что и switch-выражение.
Операторов перехода, позволяющих прервать естественный порядок выполнения операторов блока, в языке C# имеется несколько.
Оператор goto имеет простой синтаксис и семантику:
goto [метка|case константное_выражение|default];
Все операторы языка C# могут иметь метку - уникальный идентификатор, предшествующий оператору и отделенный от него символом двоеточия. Передача управления помеченному оператору - это классическое использование оператора goto. Два других способа использования goto (передача управления в case или default-ветвь) используются в операторе switch.
Оператор break может стоять в теле цикла или завершать case-ветвь в операторе switch. При выполнении оператора break в теле цикла завершается выполнение самого внутреннего цикла. В теле цикла, чаще всего, оператор break помещается в одну из ветвей оператора if, проверяющего условие преждевременного завершения цикла:
public void Jumps() { int i = 1, j=1; for(i =1; i<100; i++) { for(j = 1; j<10;j++) { if (j>=3) break; } Console.WriteLine("Выход из цикла j при j = {0}", j); if (i>=3) break; } Console.WriteLine("Выход из цикла i при i= {0}", i); }//Jumps
Оператор continue используется только в теле цикла. В отличие от оператора break, завершающего внутренний цикл, continue осуществляет переход к следующей итерации этого цикла.
Еще одним оператором, относящимся к группе операторов перехода, является оператор return, позволяющий завершить выполнение процедуры или функции. Его синтаксис:
return [выражение];
Для функций его присутствие и аргумент обязательны, поскольку выражение в операторе return задает значение, возвращаемое функцией.
Его синтаксис:
for(инициализаторы; условие; список_выражений) оператор
Оператор, стоящий после закрывающей скобки, задает тело цикла. В большинстве случаев телом цикла является блок. Сколько раз будет выполняться тело цикла, зависит от трех управляющих элементов, заданных в скобках. Инициализаторы задают начальное значение одной или нескольких переменных, часто называемых счетчиками или просто переменными цикла. В большинстве случаев цикл for имеет один счетчик, но часто полезно иметь несколько счетчиков, что и будет продемонстрировано в следующем примере. Условие задает условие окончания цикла, соответствующее выражение при вычислении должно получать значение true или false. Список выражений, записанный через запятую, показывает, как меняются счетчики цикла на каждом шаге выполнения. Если условие цикла истинно, то выполняется тело цикла, затем изменяются значения счетчиков и снова проверяется условие. Как только условие становится ложным, цикл завершает свою работу. В цикле for тело цикла может ни разу не выполняться, если условие цикла ложно после инициализации, а может происходить зацикливание, если условие всегда остается истинным. В нормальной ситуации тело цикла выполняется конечное число раз.
Счетчики цикла зачастую объявляются непосредственно в инициализаторе и соответственно являются переменными, локализованными в цикле, так что после завершения цикла они перестают существовать.
В тех случаях, когда предусматривается возможность преждевременного завершения цикла с помощью одного из операторов перехода, счетчики объявляются до цикла, что позволяет анализировать их значения при выходе из цикла.
В качестве примера рассмотрим классическую задачу: является ли строка текста палиндромом. Палиндромом называется симметричная строка текста, читающаяся одинаково слева направо и справа налево. Для ее решения цикл for подходит наилучшим образом: здесь используются два счетчика - один возрастающий, другой убывающий. Вот текст соответствующей процедуры:
/// <summary> /// Определение палиндромов.Демонстрация цикла for /// </summary> /// <param name="str">текст</param> /// <returns>true - если текст является палиндромом</returns> public bool Palindrom(string str) { for (int i =0,j =str.Length-1; i<j; i++,j--) if(str[i]!=str[j]) return(false); return(true); }//Palindrom
Цикл while (выражение) является универсальным видом цикла, включаемым во все языки программирования. Тело цикла выполняется до тех пор, пока остается истинным выражение while. В языке C# у этого вида цикла две модификации - с проверкой условия в начале и в конце цикла. Первая модификация имеет следующий синтаксис:
while(выражение) оператор
Эта модификация соответствует стратегии: "сначала проверь, а потом делай". В результате проверки может оказаться, что и делать ничего не нужно. Тело такого цикла может ни разу не выполняться. Конечно же, возможно и зацикливание. В нормальной ситуации каждое выполнение тела цикла - это очередной шаг к завершению цикла.
Цикл, проверяющий условие завершения в конце, соответствует стратегии: "сначала делай, а потом проверь". Тело такого цикла выполняется, по меньшей мере, один раз. Вот синтаксис этой модификации:
do оператор while(выражение);
Рассмотрим пример, в котором участвуют обе модификации цикла while. Во внешнем цикле проверка выполняется в конце, во внутреннем - в начале. Внешний цикл представляет собой типичный образец организации учебных программ, когда в диалоге с пользователем многократно решается некоторая задача. На каждом шаге пользователь вводит новые данные, решает задачу и анализирует полученные данные. В его власти, продолжить вычисления или нет, но хотя бы один раз решить задачу ему приходится. Внутренний цикл do while используется для решения уже известной задачи с палиндромами. Вот текст соответствующей процедуры:
/// <summary> /// Два цикла: с проверкой в конце и в начале. /// Внешний цикл - образец многократно решаемой задачи. /// Завершение цикла определяется в диалоге /// с пользователем. /// </summary> public void Loop() { string answer, text; do { Console.WriteLine("Введите слово"); text = Console.ReadLine(); int i =0, j = text.Length-1; while ((i<j) && (text[i] == text[j])) {i++; j--;} if (text[i] == text[j]) Console.WriteLine(text +" - это палиндром!"); else Console.WriteLine(text +" - это не палиндром!"); Console.WriteLine("Продолжим? (yes/no)"); answer = Console.ReadLine(); } while(answer =="yes"); }//Loop
Новым видом цикла является цикл foreach, удобный при работе с массивами, коллекциями и другими подобными контейнерами данных. Его синтаксис:
foreach(тип идентификатор in контейнер) оператор
Цикл работает в полном соответствии со своим названием - тело цикла выполняется для каждого элемента в контейнере. Тип идентификатора должен быть согласован с типом элементов, хранящихся в контейнере данных. Предполагается также, что элементы контейнера (массива, коллекции) упорядочены. На каждом шаге цикла идентификатор, задающий текущий элемент контейнера, получает значение очередного элемента в соответствии с порядком, установленным на элементах контейнера. С этим текущим элементом и выполняется тело цикла - выполняется столько раз, сколько элементов находится в контейнере. Цикл заканчивается, когда полностью перебраны все элементы контейнера.
Приведенный материал взят с сайтов:
Марченко А.Л. Введение в программирование на C# 2.0