Программирование на языке высокого уровня C#

Состав языка и типы данных

Показывать лекцию целиком

Состав языка

Презентацию к данной лекции Вы можете скачать здесь.

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

Алфавит и лексемы

Все тексты на языке пишутся с помощью его алфавита. В C# используется кодировка символов Unicode. Кодировкой, или кодовой таблицей (character set), называется соответствие между символами и кодирующими их числами. Кодировка Unicode позволяет представить символы всех существующих алфавитов одновременно. Каждому символу соответствует свой уникальный код.

Алфавит C# включает:

  • буквы (латинские и национальных алфавитов) и символ подчеркивания ( _ ), который употребляется наряду с буквами;
  • цифры ;
  • специальные символы, например +, *, { и ;
  • пробельные символы (пробел и символы табуляции);
  • символы перевода строки.
  • Из символов составляются более крупные строительные блоки: лексемы, директивы препроцессора и комментарии.

    Лексема (token) — это минимальная единица языка, имеющая самостоятельный смысл. Существуют следующие виды лексем:

  • имена ( идентификаторы );
  • ключевые слова ;
  • знаки операций ;
  • разделители ;
  • литералы ( константы ).
  • Лексемы языка программирования аналогичны словам естественного языка. Например, лексемами являются число 128 (но не его часть 12), имя Vasia, ключевое слово goto и знак операции сложения  +. Далее мы рассмотрим лексемы подробнее.

    Директивы препроцессора пришли в C# из его предшественника — языка С++. Препроцессором называется предварительная стадия компиляции, на которой формируется окончательный вид исходного текста программы. Например, с помощью директив (инструкций, команд) препроцессора можно включить или выключить из процесса компиляции фрагменты кода. Директивы препроцессора не играют в C# такой важной роли, как в С++.

    Комментарии предназначены для записи пояснений к программе и формирования документации. Правила записи комментариев мы рассмотрим чуть позже.

    Из лексем составляются выражения и операторы. Выражение задает правило вычисления некоторого значения. Например, выражение a + b задает правило вычисления суммы двух величин.

    Оператор задает законченное описание некоторого действия, данных или элемента программы. Например:

    int a;

    Это — оператор описания целочисленной переменной a.

    Идентификаторы

    Имена, или идентификаторы, служат для того чтобы обращаться к программным объектам и различать их, то есть идентифицировать. В идентификаторе могут использоваться буквы, цифры и символ подчеркивания. Прописные и строчные буквы различаются, например, hacker, Hacker и hAcKeR  — три разных имени.

    Первым символом идентификатора может быть буква или знак подчеркивания, но не цифра. Длина идентификатора не ограничена. Пробелы внутри имен не допускаются.

    В идентификаторах C# разрешается использовать, помимо латинских букв, буквы национальных алфавитов. Например, правильными являются идентификаторы Фёкла и calc. Более того, можно применять даже так называемые escape-последовательности Unicode, то есть представлять символ с помощью его кода в шестнадцатеричном виде с префиксом \u, например, \u00F2.

    Примечание

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

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

    Ключевые слова

    Ключевые слова — это зарезервированные идентификаторы, которые имеют специальное значение для компилятора. Их можно использовать только в том смысле, в котором они определены. Список ключевых слов C# приведен в таблице 2.1. Как видите, их не так уж и много!

    Ключевые слова C#
    abstract as base bool break
    byte case catch char checked
    class const continue decimal default
    delegate do double else enum
    event explicit extern false finally
    fixed float for foreach goto
    if implicit in int interface
    internal is lock long namespace
    new null object operator out
    override params private protected public
    readonly ref return sbyte sealed
    short sizeof stackalloc static string
    struct switch this throw true
    try typeof uint ulong unchecked
    unsafe ushort using virtual void
    volatile while

    Знаки операций и разделители

    Знак операции — это один или более символов, определяющих действие над операндами. Внутри знака операции пробелы не допускаются. Например, в выражении a += b знак += является знаком операции, а a и b  — операндами. Символы, составляющие знак операций, могут быть специальными, например, +, , | и <, и буквенными, такими как as или new.

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

    Разделители используются для разделения или, наоборот, группирования элементов. Примеры разделителей: скобки, точка, запятая. Ниже перечислены все знаки операций и разделители, использующиеся в C#:

    { }  [ ]  ( )  .  ,  :  ;  +  -  *  /  %    |  ^  !  ~  =
    <  >  ?  ++  --    ||  <<  >>  ==  !=  <=  >=  +=  -=  *=  /=  %=
    =  |=  ^=  <<=  >>=  ->

    Литералы (константы)

    Литералами, или константами, называют неизменяемые величины. В C# есть логические, целые, вещественные, символьные и строковые константы, а также константа null. Компилятор, выделив константу в качестве лексемы, относит ее к одному из типов данных по ее внешнему виду. Программист может задать тип константы и самостоятельно.

    Описание и примеры констант каждого типа приведены в таблице 2.2. Примеры, иллюстрирующие наиболее часто употребляемые формы констант, выделены полужирным шрифтом.

    Константы в C#
    Константа Описание Примеры
    Логическая true (истина) или false (ложь)
    true
    false
    Целая Десятичная: последовательность десятичных цифр (0, 1, 2, 3, 4, 5, 6, 7, 8, 9), за которой может следовать суффикс (U, u, L, l, UL, Ul, uL, ul, LU, Lu, lU, lu)
    8 0 199226
    8u 0Lu 199226L
    Шестнадцатеричная: символы или , за которыми следуют шестнадцатеричные цифры (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F), а за цифрами, в свою очередь, может следовать суффикс (U, u, L, l, UL, Ul, uL, ul, LU, Lu, lU, lu)
    0xA 0x1B8 0X00FF
    0xAU 0x1B8LU
    0X00FFl
    Вещественная С фиксированной точкой: [цифры][.][цифры][суффикс] Суффикс — один из символов F, f, D, d, M, m
    5.7 .001 35.
    5.7F .001d 35.
    5F .001f 35m
    С порядком: [цифры][.][цифры]{E|e}[+|–][цифры] [суффикс] Суффикс — один из символов F, f, D, d, M, m
    0.2E6 .11e+3 5E-10
    0.2E6D .11e–3
    5E10
    Символьная Символ, заключенный в апострофы
    'A' 'ю' '*'
    '\0' '\n'
    '\xF' '\x74'
    '\uA81B'
    Строковая Последовательность символов, заключенная в кавычки
    "Здесь был Vasia"
    "\tЗначение r = \xF5 \n"
    "Здесь был \u0056\u0061"
    "C:\\temp\\file1.txt"
    @"C:\temp\file1.txt"
    Константа null Ссылка, которая не указывает ни на какой объект null

    Логических литералов всего два: true и false. Они широко используются в качестве признаков наличия или отсутствия чего-либо.

    Целые литералы могут быть представлены либо в десятичной, либо в шестнадцатеричной системе счисления, а вещественные  — только в десятичной системе, но в двух формах: с фиксированной точкой и с порядком. Вещественная константа с порядком представляется в виде мантиссы и порядка. Мантисса записывается слева от знака экспоненты ( E или e ), порядок — справа от знака. Значение константы определяется как произведение мантиссы и возведенного в указанную в порядке степень числа 10 (например, 1.3e2 = 1,3 x 102 = 130). Пробелы внутри константы не допускаются.

    Если требуется сформировать отрицательную целую или вещественную константу, то перед ней ставится знак унарной операции изменения знака ( ), например, –218.

    Символьная константа представляет собой любой символ в кодировке Unicode. Символьные константы записываются в одной из четырех форм:

  • "обычный" символ, имеющий графическое представление (кроме апострофа и символа перевода строки) — 'A', 'ю', '*' ;
  • управляющая последовательность — '\0', '\n' ;
  • символ в виде шестнадцатеричного кода — '\xF', '\x74' ;
  • символ в виде escape-последовательности Unicode — '\uA81B'.
  • Управляющей последовательностью, или простой escape-последовательностью, называют определенный символ, предваряемый обратной косой чертой. Управляющая последовательность интерпретируется как одиночный символ и используется для представления:

  • кодов, не имеющих графического изображения (например, \n  — переход в начало следующей строки);
  • символов, имеющих специальное значение в строковых и символьных литералах, например, апострофа (  '  ).
  • Допустимые значения последовательностей приведены в таблице 2.3.

    Управляющие последовательности в С#
    Вид Наименование
    \a Звуковой сигнал
    \b Возврат на шаг
    \f Перевод страницы (формата)
    \n Перевод строки
    \r Возврат каретки
    \t Горизонтальная табуляция
    \v Вертикальная табуляция
    \\ Обратная косая черта
    \' Апостроф
    \" Кавычка
    \0 Нуль-символ

    Символ, представленный в виде шестнадцатеричного кода, начинается с префикса \x, за которым следует код символа. Числовое значение должно находиться в диапазоне от 0 до 216 – 1, иначе возникает ошибка компиляции.

    Escape-последовательности Unicode служат для представления символа в кодировке Unicode с помощью его кода в шестнадцатеричном виде с префиксом \u или \U, например, \u00F2, \U00010011. Коды в диапазоне от \U10000 до \U10FFFF представляются в виде двух последовательных символов; коды, превышающие \U10FFFF, не поддерживаются.

    Управляющие последовательности обоих видов могут использоваться и в строковых константах, называемых иначе строковыми литералами. Например, если требуется вывести несколько строк, можно объединить их в один литерал, отделив одну строку от другой символами \n:

    "Никто не доволен своей\nвнешностью, но каждый доволен\nсвоим умом"

    Этот литерал при выводе будет выглядеть так:

    Никто не доволен своей
    внешностью, но каждый доволен
    своим умом

    В C# есть и второй вид литералов — дословные (verbatim strings). Эти литералы предваряются символом @, который отключает обработку управляющих последовательностей и позволяет получать строки в том виде, в котором они записаны. Чаще всего дословные литералы применяются при задании полного пути файла. Посмотрите, насколько лучше воспринимается второй вариант записи одного и того же пути:

    "C:\\app\\bin\\debug\\a.exe"
    @"C:\app\bin\debug\a.exe"

    Строка может быть пустой (записывается парой смежных двойных кавычек "" ). Пустая символьная константа недопустима.

    Константа null представляет собой значение, задаваемое по умолчанию для величин так называемых ссылочных типов, которые мы рассмотрим далее в этой лекции.

    Комментарии

    Комментарии предназначены для записи пояснений к программе и формирования документации. Компилятор комментарии игнорирует. Внутри комментария можно использовать любые символы. В C# есть два вида комментариев: однострочные и многострочные.

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

    Кроме того, в языке есть еще одна разновидность комментариев, которые начинаются с трех подряд идущих символов косой черты ( /// ). Они предназначены для формирования документации к программе в формате XML. Компилятор извлекает эти комментарии из программы, проверяет их соответствие правилам и записывает их в отдельный файл.

    Типы данных

    Данные, с которыми работает программа, хранятся в оперативной памяти. Естественно, что компилятору необходимо точно знать, сколько места они занимают, как именно закодированы и какие действия с ними можно выполнять. Все это задается при описании данных с помощью типа.

    Тип данных однозначно определяет:

  • внутреннее представление данных, а следовательно и множество их возможных значений ;
  • допустимые действия над данными (операции и функции).
  • Например, целые и вещественные числа, даже если они занимают одинаковый объем памяти, имеют совершенно разные диапазоны возможных значений.

    Каждое выражение в программе имеет определенный тип. Компилятор использует информацию о типе при проверке допустимости описанных в программе действий.

    Память, в которой хранятся данные во время выполнения программы, делится на две области: стек (stack) и динамическая область, или хип (heap), чаще называемый кучей (поскольку этот термин мне не нравится, я его использовать не буду). Стек используется для хранения величин, память под которые выделяет компилятор, а в динамической области память резервируется и освобождается во время выполнения программы с помощью специальных команд. Основным местом для хранения данных в C# является хип.

    Классификация типов

    Типы можно классифицировать по разным признакам. Если принять за основу строение элемента, все типы можно разделить на простые (не имеют внутренней структуры) и структурированные (состоят из элементов других типов). По своему "создателю" типы можно разделить на встроенные (стандартные) и определяемые программистом. По способу хранения значений типы делятся на значимые, или типы-значения, и ссылочные. Рассмотрим в первую очередь встроенные типы C#.

    Встроенные типы

    Встроенные типы не требуют предварительного определения. Для каждого типа существует ключевое слово, которое используется при описании переменных, констант и т. д. Встроенные типы C# приведены в таблице 2.4. Они однозначно соответствуют стандартным классам библиотеки .NET, определенным в пространстве имен System. Как видно из таблицы, существуют несколько вариантов представления целых и вещественных величин. Программист выбирает тип каждой величины, используемой в программе, с учетом необходимого ему диапазона и точности представления данных.

    Встроенные типы C#
    Название Ключевое слово Тип .NET Диапазон значений Описание Размер, битов
    Логический тип bool Boolean true, false
    Целые типы sbyte SByte От –128 до 127 Со знаком 8
    byte Byte От 0 до 255 Без знака 8
    short Int16 От –32768 до 32767 Со знаком 16
    ushort UInt16 От 0 до 65535 Без знака 16
    int Int32 От –2 x 109 до 2 x 109 Со знаком 32
    uint UInt32 От 0 до 4 x 109 Без знака 32
    long Int64 От –9 x 1018 до 9 x 1018 Со знаком 64
    ulong UInt64 От 0 до 18 x 1018 Без знака 64
    Символьный тип char Char От U+0000 до U+ffff Unicode-символ 16
    Вещественные float Single От 1.5 x 10-45 до 3.4 x 1038 7 цифр 32
    double Double От 5.0 x 10-324 до 1.7 x 10308 15–16 цифр 64
    Финансовый тип decimal Decimal От 1.0 x 10-28 до 7.9 x 1028 28–29 цифр 128
    Строковый тип string String Длина ограничена объемом доступной памяти Строка из Unicode-символов
    Тип object object Object Можно хранить все, что угодно Всеобщий предок

    Примечание

    Вещественные типы и финансовый тип являются знаковыми. Для них в столбце "диапазон значений" приведены абсолютные величины допустимых значений.

    Логический, или булев, тип содержит всего два значения: true (истина) и false (ложь). Их внутреннее представление программиста интересовать не должно. Все целые и вещественные типы вместе с символьным и финансовым можно назвать арифметическими типами.

    Внутреннее представление величины целого типа  — целое число в двоичном коде. В знаковых типах старший бит числа интерпретируется как знаковый (0 — положительное число, 1 — отрицательное).

    Вещественные типы, или типы данных с плавающей точкой, хранятся в памяти компьютера иначе, чем целочисленные. Внутреннее представление вещественного числа состоит из двух частей — мантиссы и порядка, причем каждая часть имеет знак. Длина мантиссы определяет точность числа, а длина порядка — его диапазон. Все вещественные типы могут представлять как положительные, так и отрицательные числа. Чаще всего в программах используется тип double, поскольку его диапазон и точность покрывают большинство потребностей. Этот тип имеют вещественные литералы и многие стандартные математические функции.

    Тип decimal предназначен для денежных вычислений, в которых критичны ошибки округления. Величины типа decimal позволяют хранить 28–29 десятичных разрядов. Тип decimal не относится к вещественным типам, у них различное внутреннее представление. Величины денежного типа даже нельзя использовать в одном выражении с вещественными без явного преобразования типа. Использование величин финансового типа в одном выражении с целыми допускается.

    Любой встроенный тип C# соответствует стандартному классу библиотеки .NET, определенному в пространстве имен System. Имя этого класса приведено в третьем столбце таблицы 2.4. Везде, где используется имя встроенного типа, его можно заменить именем класса библиотеки.

    Типы литералов

    Литералы (константы) тоже имеют тип. Если значение целого литерала находится внутри диапазона допустимых значений типа int, литерал рассматривается как int, иначе он относится к наименьшему из типов uint, long или ulong, в диапазон значений которого он входит. Вещественные литералы по умолчанию относятся к типу double.

    Например, константа 10 относится к типу int, хотя для ее хранения достаточно и байта, а константа 2147 483 648 будет определена как uint. Для явного задания типа литерала служит суффикс, например, 1.1f, 1UL, 1000m. Явное задание применяется в основном для уменьшения количества неявных преобразований типа, выполняемых компилятором.

    Типы-значения и ссылочные типы

    Чаще всего типы C# разделяют ). Элементы типов-значений, или значимых типов (value types), представляют собой просто последовательность битов в памяти, необходимый объем которой выделяет компилятор. Иными словами, величины значимых типов хранят свои значения непосредственно. Величина ссылочного типа хранит не сами данные, а ссылку на них (адрес, по которому расположены данные). Сами данные хранятся в хипе.

    Внимание

    Несмотря на различия в способе хранения, и типы-значения, и ссылочные типы являются потомками общего базового класса object.

    (рис 2.1) Классификация типов данных C# по способу хранения

    рис 2.2 иллюстрирует разницу между величинами значимого и ссылочного типов. Одни и те же действия над ними выполняются по-разному. Рассмотрим в качестве примера проверку на равенство. Величины значимого типа равны, если равны их значения. Величины ссылочного типа равны, если они ссылаются на одни и те же данные (на рисунке b и c равны, но a не равно b даже при одинаковых значениях). Из этого следует, что если изменить значение одной величины ссылочного типа, это может отразиться на другой.

    (рис 2.2) Хранение в памяти величин значимого и ссылочного типов

    Упаковка и распаковка

    Для того чтобы величины ссылочного и значимого типов могли использоваться совместно, необходимо иметь возможность преобразования из одного типа в другой. Преобразование из типа-значения в ссылочный тип называется упаковкой (boxing), обратное преобразование — распаковкой (unboxing).

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

    Вопросы и задания для самостоятельной работы студента

  • Перечислите и опишите лексемы языка C#.
  • Перечислите и опишите литералы языка C#.
  • Перечислите встроенные типы данных и опишите их.
  • Приведите классификацию типов данных C#.
  • Какие элементы стандартных классов .NET, соответствующих встроенным типам языка, вы знаете?
  • Опишите отличия значимых и ссылочных типов. В чем состоят процессы упаковки и распаковки?
  • Вернуться к учебному плану