Программирование / 3 Основные понятия С#
.pdf3. Основные понятия С#
Наибольшие трудности в изучении языка программирования вызывает то обстоятельство, что ни один из его элементов не существует обособленно. Напротив, все элементы языка действуют совместно. Такая взаимосвязанность затрудняет рассмотрение одного аспекта C# безотносительно к другому. Поэтому сначала рассмотрим основные принципы объектноориентированного программирования.
3.1. Объектно-ориентированное программирование
Основным понятием C# является объектно-ориентированное программирование (ООП). Методика ООП неотделима от C#, и поэтому все программы на C# являются объектноориентированными хотя бы в самой малой степени. В связи с этим очень важно и полезно усвоить основополагающие принципы ООП, прежде чем приступать к написанию самой простой программы на C#.
ООП представляет собой эффективный подход к программированию. Методики программирования претерпели существенные изменения с момента изобретения компьютера, постепенно приспосабливаясь, главным образом, к повышению сложности программ. Повторим, вкратце, историю методов программирования.
Когда, появились первые ЭВМ, программирование заключалось в ручном переключении на разные двоичные машинные команды с переднего пульта управления ЭВМ. Такой подход был вполне оправданным, поскольку программы состояли всего из нескольких сотен команд. Дальнейшее усложнение программ привело к разработке языка ассемблера, который давал программистам возможность работать с более сложными программами, используя символическое представление отдельных машинных команд. Постоянное усложнение программ вызвало потребность в разработке и внедрении в практику программирования таких языков высокого уровня, как, например, FORTRAN и COBOL, которые предоставляли программистам больше средств для того, чтобы как-то справиться с постоянно растущей сложностью программ. Но как только возможности этих первых языков программирования были полностью исчерпаны, появились разработки языков структурного программирования, в том числе и С.
На каждом этапе развития программирования появлялись методы и инструментальные средства для "обуздания" растущей сложности программ. И на каждом таком этапе новый подход вбирал в себя все самое лучшее из предыдущих, знаменуя собой прогресс в программировании. Это же можно сказать и об ООП. До ООП многие проекты достигали (а иногда и превышали) предел, за которым структурный подход к программированию оказывался уже неработоспособным. Поэтому для преодоления трудностей, связанных с усложнением программ, и возникла потребность в ООП.
ООП вобрало в себя все самые лучшие идеи структурного программирования, объединив их с рядом новых понятий. В итоге появился новый и лучший способ организации программ. В самом общем виде программа может быть организована одним из двух способов: вокруг кода (т.е. того, что фактически происходит) или же вокруг данных (т.е. того, что подвергается воздействию). Программы, созданные только методами структурного программирования, как правило, организованы вокруг кода. Такой подход можно рассматривать "как код, воздействующий на данные".
Совсем иначе работают объектно-ориентированные программы. Они организованы вокруг данных, исходя из главного принципа: "данные управляют доступом к коду". В объектноориентированном языке программирования определяются данные и код, которому разрешается воздействовать на эти данные. Следовательно, тип данных точно определяет операции, которые могут быть выполнены над данными.
Для поддержки принципов ООП все объектно-ориентированные языки программирования, в том числе и С#, должны обладать тремя общими свойствами: инкапсуляцией, полиморфизмом и наследованием. Рассмотрим каждое из этих свойств в отдельности.
3.1.1. Инкапсуляция
Инкапсуляция — это механизм программирования, объединяющий вместе код и данные, которыми он манипулирует, исключая как вмешательство извне, так и неправильное использование данных. В объектно-ориентированном языке данные и код могут быть объединены в совершенно автономный черный ящик. Внутри такого ящика находятся все необходимые данные и код. Когда код и данные связываются вместе подобным образом, создается объект. Иными словами, объект — это элемент, поддерживающий инкапсуляцию.
В объекте код, данные или же и то и другое могут быть закрытыми или же открытыми. Закрытые данные или код известны и доступны только остальной части объекта. Это означает, что закрытые данные или код недоступны части программы, находящейся за пределами объекта. Если же данные или код оказываются открытыми, то они доступны другим частям программы, хотя и определены внутри объекта. Как правило, открытые части объекта служат для организации управляемого интерфейса с закрытыми частями.
Основной единицей инкапсуляции в C# является класс, который определяет форму объекта. Он описывает данные, а также код, который будет ими оперировать. В C# описание класса служит для построения объектов, которые являются экземплярами класса. Следовательно, класс, по существу, представляет собой ряд схематических описаний способа построения объекта.
Код и данные, составляющие вместе класс, называют членами. Данные, определяемые классом, называют полями, или переменными экземпляра. А код, оперирующий данными, содержится в функциях-членах, самым типичным представителем которых является метод. В C# метод служит в качестве аналога подпрограммы. (К числу других функций-членов относятся свойства, события и конструкторы). Таким образом, методы класса содержат код, воздействующий на поля, определяемые этим классом.
3.1.2. Полиморфизм
Полиморфизм, в переводе с греческого означает "множество форм", — это свойство, позволяющее одному интерфейсу получать доступ к общему классу действий. Простым примером полиморфизма может служить руль автомашины, который выполняет одни и те же функции своеобразного интерфейса независимо от вида применяемого механизма управления автомашиной. Это означает, что руль действует одинаково независимо от вида рулевого управления: прямого действия, с усилением или реечной передачей. Следовательно, при вращении руля влево автомашина всегда поворачивает влево, какой бы вид управления в ней ни применялся. Главное преимущество единообразного интерфейса заключается в том, что, зная, как обращаться с рулем, вы сумеете водить автомашину любого типа.
В более общем смысле понятие полиморфизма нередко выражается следующим образом: "один интерфейс — множество методов". Это означает, что для группы взаимосвязанных действий можно разработать общий интерфейс. Полиморфизм помогает упростить программу, позволяя использовать один и тот же интерфейс для описания общего класса действий. Выбрать конкретное действие (т.е. метод) в каждом отдельном случае — это задача компилятора. Программисту не нужно делать это самому. Ему достаточно запомнить и правильно использовать общий интерфейс.
3.1.3. Наследование
Наследование представляет собой процесс, в ходе которого один объект приобретает свойства другого объекта. Это очень важный процесс, поскольку он обеспечивает принцип иерархической классификации. Если вдуматься, то большая часть знаний поддается систематизации благодаря иерархической классификации по нисходящей. Например, сорт яблок "Джонатан" входит в общую классификацию сортов яблок, которые, в свою очередь, относятся к классу фруктов, а те — к еще более крупному классу пищевых продуктов. Это означает, что класс пищевых продуктов обладает рядом свойств (съедобности, питательности и т.д.), которые по логике вещей распространяются и на его подкласс фруктов. Помимо этих свойств, класс фруктов обладает своими собственными свойствами (сочностью, сладостью и т.д.), которыми он отличается от других пищевых продуктов. У класса яблок имеются свои характерные особенности (растут на деревьях, не в тропиках и т.д.). Таким образом, сорт яблок "Джонатан" наследует
свойства всех предшествующих классов, обладая в то же время свойствами, присущими только этому сорту яблок, например красной окраской кожицы с желтым бочком и характерным ароматом и вкусом =)).
Если не пользоваться иерархиями, то для каждого объекта пришлось бы явно определять все его свойства. А если воспользоваться наследованием, то достаточно определить лишь те свойства, которые делают объект особенным в его классе. Он может также наследовать общие свойства своего родителя. Следовательно, благодаря механизму наследования один объект становится отдельным экземпляром более общего класса.
3.2.Базовый синтаксис C#
1.Компиляторы C# не обращают внимания на дополнительные интервалы в коде, проставляемые с помощью символов пустой строки, возврата каретки (переход на новую строку) и табуляции (все вместе они называются пробельными символами). Это предоставляет разработчику огромную свободу в плане форматирования кода, хотя следование определенным правилам, конечно же, может помочь делать код более удобным для чтения.
2.Код на C# состоит из ряда операторов, каждый из которых оканчивается символом точки с запятой. Поскольку пробелы игнорируются, в одной строке может размещаться сразу несколько операторов, но для удобочитаемости все-таки принято операторы размещать на разных строках. Тем не менее, вполне допустимо (и довольно распространено) использовать операторы, занимающие по несколько строк кода.
3.C# является языком с блочной структурой, т.е. все операторы относятся к какому-то блоку кода. В каждом блоке, который отделяется от остальной части кода с помощью фигурных скобок ( { и } ), может содержаться любое количество операторов или вообще ни одного.
Сопровождать символы фигурных скобок символами точки с запятой не нужно. Например, простой блок кода на C# может иметь следующий вид:
{
<строка_кода_1>; <строка_кода_1>; <строка_кода_1>;
}
Здесь разделы <строка_кода_n> фактическими фрагментами кода C# не являются, а просто представляют собой метки-заполнители, указывающие на место, где должны находиться С#- операторы.
Добавление отступов для удобства чтения кода является стандартным приемом и в принципе по умолчанию выполняется средой Visual Studio (VS) автоматически. В общем, для каждого блока кода используется свое количество отступов, т.е. своя степень смещения вправо. Блоки кода еще также могут и вкладываться один в другой, т.е. одни блоки могут содержать другие блоки, и в таком случае вложенные блоки, соответственно, смещаются на большее количество отступов вправо:
{
<строка_кода_1>;
{
<строка_кода_2>; <строка_кода_3>;
}
<строка_кода_4 >;
}
Разумеется, такой стиль ни в коем случае не является обязательным. Однако если его не придерживаться, код может становиться очень запутанным.
4.Одним из важнейших элементов любого кода являются комментарии. Собственно кодом на C# комментарии не считаются, но они позволяют добавлять в код описательный текст на обычном языке, который компилятором игнорируется. При создании длинных разделов кода очень удобно добавлять пометки, напоминающие о том, какие именно операции в них
выполняются, вроде “эта строка кода предлагает пользователю ввести число” или “этот раздел кода написан кем-то”. C# позволяет делать это несколькими способами:
1) Для обозначения многострочных комментариев в начале комментария ставятся символы «/*», а в конце — символы «*/». Эти символы могут находиться как на одной, так и на разных строках (все находящиеся между ними строки тоже считаются комментарием). Единственной комбинацией символов, которую нельзя вводить в теле комментария, является «*/», потому что она воспринимается как маркер конца строки.
2) Для обозначения однострочного комментария используется комбинации символов «//». После этой комбинации и до конца строки все считается комментарием. Такой способ комментирования удобно применять для документирования операторов, поскольку он позволяет размещать операторы и комментарии в одной строке.
3)Третий вид комментариев аналогичен предыдущему, но начинается не с 2-х слешей,
ас трех: «///». При обычных обстоятельствах такие комментарии компилятором игнорируются, подобно все остальным комментариям. Но среду VS можно настраивать так, чтобы при компиляции проекта она извлекала текст, идущий после таких комментариев, и создавала для него отформатированный специальным образом текстовый файл, который затем можно применять для создания документации. Этот процесс здесь рассматривать не будем.
5.Очень важным моментом, является чувствительность C# к регистру. В отличие от некоторых других языков, в языке C# код обязательно нужно вводить в правильном регистре, поскольку использование прописной буквы вместо заглавной может привести к невозможности скомпилировать проект. К счастью, VS очень помогает в вопросах, касающихся ввода кода, и в большинстве случаев догадывается (настолько, насколько может догадываться компьютерная программа) о том, что пытается сделать программист. По мере того, как программист вводит код, она предлагает команды, которые он может использовать, и старается исправлять ошибки, связанные с применением неправильного регистра.
3.3. Основные конструкции программирования на C#
При рассмотрении языка C# будем использовать среду разработки Microsoft Visual Studio 2010. Express версия данного пакета доступна бесплатно, и образ установочного диска можно скачать с сайта www.microsoft.com/visualstudio/ru-ru. Для рассмотрения примеров и типовых задач будем использовать консольное приложение (проект – Console Application). Процесс создания и компилирования проектов подробно описан в лабораторной работе №1.
3.3.1.Простая программа на C#
Вязыке C# вся логика программы должна содержаться внутри определения какого-то типа (тип представляет собой общий термин, которым обозначается любой элемент из множества
{класс, интерфейс, структура, перечисление, делегат}). В отличие от многих других языков, в C# не допускается создавать ни глобальных функций, ни глобальных элементов данных. Вместо этого требуется, чтобы все члены данных и методы содержались внутри определения типа.
Для начала давайте создадим новый проект типа Console Application (Консольное приложение) по имени SimpleCSharpApp. Ниже показан исходный код Program.cs:
using System;
namespace SimpleCSharpApp
{
class Program
{
static void Main()
{
}
}
}
Внесем изменения в метод Main() класса Program, добавив в него несколько операторов:
using System;
namespace SimpleCSharpApp
{
class Program
{
static void Main()
{
// Вывод простого сообщения пользователю
Console.WriteLine("First Console App"); Console.WriteLine("Hello world"); Console.WriteLine();
/* Ожидание нажатия клавиши <Enter> перед завершением работы */
Console.ReadLine();
}
}
}
Таким образом, после компиляции и запуска созданного приложения запуститься консольное приложение, в котором будет выведены 2 строки текста. Данное приложение будет активно до тех пор, пока пользователь не нажмет клавишу <Enter>.
В файлах с исходным текстом программ на C# условно принято расширение *.cs, и это условие следует соблюдать. Кроме того, многие программисты называют файлы с исходным текстом своих программ по имени основного класса, определенного в программе. Именно поэтому в рассматриваемом здесь примере было выбрано имя файла Program.cs. Но имена программ на C# могут быть произвольными, поэтому вы вольны сами выбирать для них имена.
Итак, анализируемая программа начинается со строки:
using System;
Эти строки означают, что в программе используется пространство имен System. В C# пространство имен определяет область объявлений. Благодаря пространству имен одно множество имен отделяется от других. По существу, имена, объявляемые в одном пространстве имен, не вступают в конфликт с именами, объявляемыми в другом пространстве имен. В анализируемой программе используется пространство имен System, которое зарезервировано для элементов, связанных с библиотекой классов среды .NET Framework, применяемой в С#. Ключевое слово using просто констатирует тот факт, что в программе используются имена в заданном пространстве имен. А следующая строка:
namespace SimpleCSharpApp
определяет собственное пространство имен. Перейдем к следующей строке программы.
class Program
В этой строке ключевое слово class служит для объявления вновь определяемого класса. Как упоминалось выше, класс является основной единицей инкапсуляции в С#, а Program — это имя класса. Определение класса начинается с открывающей фигурной скобки «{» и заканчивается закрывающей фигурной скобкой «}». Элементы, заключенные в эти фигурные скобки, являются членами класса. Не вдаваясь пока что в подробности, достаточно сказать, что в C# большая часть действий, выполняемых в программе, происходит именно в классе.
Перейдем к анализу следующей строки программы.
static void Main()
Эта строка начинается с метода Main(). Как упоминалось выше, в C# подпрограмма называется методом. И, именно с этой строки начинается выполнение программы. Выполнение всех приложений C# начинается с вызова метода Main(). Разбирать полностью значение каждого элемента данной строки пока что не имеет смысла, потому что для этого нужно знать ряд других средств С#. Но поскольку данная строка используется во многих примерах программ, то проанализируем ее вкратце.
Данная строка начинается с ключевого слова static. Метод, определяемый ключевым словом static, может вызываться до создания объекта его класса. Необходимость в этом объясняется тем, что метод Main() вызывается при запуске программы. Ключевое слово void указывает на то, что метод Main() не возвращает значение. В дальнейшем вы узнаете, что методы могут также возвращать значения. Пустые круглые скобки после имени метода Main означают, что этому методу не передается никакой информации. Вообще, методу Main() можно передать информацию, но в данном примере этого не делается.
На следующей строке находится символ «{», обозначающий начало тела метода Main(). Весь код, составляющий тело метода, находится между открывающими и закрывающими фигурными скобками.
Рассмотрим следующую строку программы. Обратите внимание на то, что она находится внутри метода Main().
// Вывод простого сообщения пользователю
Это однострочный комментарий. Следующая строка:
Console.WriteLine("First Console App");
Оператор осуществляет вывод на экран текстовой строки “First Console App” и перевод курсора на следующую строку. Сам вывод выполняется встроенным методом WriteLine(). В данном примере метод WriteLine() выводит на экран строку, которая ему передается в качестве аргумента. Помимо текстовых строк, метод WriteLine() позволяет выводить на экран другие виды информации. Анализируемая строка начинается с Console — имени предопределенного класса, поддерживающего ввод-вывод на консоль. Сочетание обозначений Console и WriteLine() указывает компилятору на то, что метод WriteLine() является членом класса Console. Применение в C# объекта для определения вывода на консоль служит еще одним свидетельством объектноориентированного характера этого языка программирования.
Обратите внимание на то, что оператор, содержащий вызов метода WriteLine(), оканчивается точкой с запятой, как, впрочем, и рассматривавшаяся ранее директива using System. Как правило, операторы в C# оканчиваются точкой с запятой. Исключением из этого правила служат блоки, которые начинаются символом «{» и заканчиваются символом «}». Именно поэтому строки программы с этими символами не оканчиваются точкой с запятой. Блоки обеспечивают механизм группирования операторов и рассматриваются далее.
Затем выводится еще одна строка “Hello world”:
Console.WriteLine("Hello world");
Обратим внимание на строку:
Console.WriteLine();
Методу WriteLine() не передаются аргументы, то есть он выводит пустую строку (по сути, просто переводит курсор на следующую строку). Затем идет многострочный комментарий:
/* Ожидание нажатия клавиши <Enter> перед завершением работы */
Как и в большинстве других языков программирования, в C# допускается вводить комментарии в файл с исходным текстом программы. Содержимое комментария игнорируется
компилятором. Но, с другой стороны, в комментарии дается краткое описание или пояснение работы программы для всех, кто читает ее исходный текст. Разумеется, в комментариях к приложениям обычно поясняется работа отдельных частей программы или же функции конкретных средств.
И, наконец, последний оператор:
Console.ReadLine();
здесь вызывается метод Console.ReadLine(), чтобы окно командной строки, запускаемое в IDEсреде Visual Studio 2010, оставалось видимым во время сеанса отладки до тех пор, пока не будет нажата клавиша <Enter>. Вообще, этот метод ожидает, пока пользователь введет значение передаваемого методу аргумента. Но поскольку в нашем случае аргументов нет, ожидается просто нажатие клавиши <Enter>.
Первый символ «}», в анализируемой программе, завершает метод Main(), второй — определение класса Program, а третий – пространство имен SimpleCSharpApp.
И наконец, в C# различаются прописные и строчные буквы. Несоблюдение этого правила может привести к серьезным осложнениям. Так, если вы неумышленно наберете main вместо Main или же writeline вместо WriteLine, анализируемая программа окажется ошибочной. Более того, компилятор C# не предоставит возможность выполнить классы, которые не содержат метод Main(), хотя и скомпилирует их. Поэтому если вы неверно наберете имя метода Main(), то получите от компилятора сообщение об ошибке, уведомляющее о том, что в исполняемом файле Program.ехе не определена точка входа.
Рассмотрим более подробно использование ключевого слова using.
3.3.2. Сборки, пространства имен, типы
Один из важнейших элементов современного программирования – библиотеки программных кодов. Главная цель таких библиотек, как MFC или Java Enterprise Edition, заключается в предоставлении разработчикам набора готовых, правильно оформленных блоков программного кода, чтобы они могли использовать их своих приложениях. Язык С#, однако, не поставляется с какой-либо специфической библиотекой кода. Вместо этого от разработчиков, использующих С#, требуется применять нейтральные к языкам библиотеки, которые поставляются в .NET. Для поддержания всех типов в библиотеках базовых классов в хорошо организованном виде в .NET широко применяется понятие пространства имен (namespace).
Под пространством имен понимается группа связанных между собой с семантической точки зрения типов, которые содержатся в сборке. Например, в пространстве имен System.IO
содержатся типы, имеющие отношение к операциям ввода-вывода, в пространстве имен System.Data – основные типы для работы с базами данных, и т.д.
Очень важно понимать, что в одной сборке (например, mscorelib.dll) может содержаться любое количество пространств имен, каждое из которых, в свою очередь, может иметь любое число типов.
В VS 2010 включена утилита Object Browser, позволяющая просматривать сборки, на которые имеются ссылки в текущем проекте, пространства имен, содержащиеся в каждой из этих сборок, типы, определенные в каждом из этих пространств имени, и члены каждого из этих типов. Важно обратить внимание на то, что в mscorlib.dll содержится очень много самых разных пространств имен (вроде System.IO), и что в каждом из них содержатся свои семантически связанные типы.
Очевидно, что главной задачей любого планирующего использовать .NET разработчика является освоение того обилия типов, которые содержатся в (многочисленных) пространствах имен .NET. Самым главным пространством имен, с которого следует начинать, является System. В этом пространстве имен содержится набор ключевых типов, которые любому разработчику .NET нужно будет эксплуатировать снова и снова. Фактически, создание функционального приложения на C# невозможно без добавления хотя бы ссылки на пространство имен System, поскольку все главные типы данных содержатся именно здесь.
Не помешает снова вспомнить, что пространства имен представляют собой не более чем удобный способ логической организации взаимосвязанных типов для упрощения работы с ними.
Давайте еще раз обратимся к пространству имен System. С человеческой точки зрения System.Console представляет класс по имени Console, содержится внутри пространства имен под названием System. Но с точки зрения исполняющей .NET это не так. Механизм исполняющей среды видит только одну лишь сущность по имени System.Console.
В C# ключевое слово using упрощает процесс добавления ссылок на типы, содержащиеся в определенном пространстве имен, то есть оно констатирует тот факт, что в программе используются имена в заданном пространстве имен.
Воспользуемся кодом нашей простой программы. Уберем первую строку
using System;
Как только мы это сделаем, VS подчеркнет имя объекта Console в строках
Console.WriteLine("First Console App"); Console.WriteLine("Hello world"); Console.WriteLine();
/* Ожидание нажатия клавиши <Enter> перед завершением работы */
Console.ReadLine();
И сообщит: «элемент Console не существует в текущем контексте». То есть в оставшихся пространствах имен System.Collections.Generic, System.Linq и System.Text; объекта Console нет.
Таким образом, придется использовать полные имена:
using System.Collections.Generic; using System.Linq;
using System.Text;
namespace SimpleCSharpApp
{
class Program
{
static void Main(string[] args)
{
// Вывод простого сообщения пользователю
System.Console.WriteLine("First Console App"); System.Console.WriteLine("Hello world"); System.Console.WriteLine();
/* Ожидание нажатия клавиши <Enter> перед завершением работы */
System.Console.ReadLine();
}
}
}
Приведем еще пример. Предположим, что требуется создать графическое настольное приложение с использованием API-интерфейса Windows Forms. В главном окне этого приложения должна визуализироваться гистограмма с информацией, получаемой из базы данных, и отображаться логотип компании. Поскольку для изучения типов в каждом пространстве имен требуются время и силы, ниже показаны некоторые из возможных кандидатов на использование в такой программе:
// Все пространства имен, которые можно использовать // для создания подобного приложения. using System; // Общие типы из библиотеки базовых классов,
using System.Drawing; |
// Типы для |
визуализации |
графики. |
||||
using System.Windows.Forms; |
// Типы для |
создания элементов пользовательского |
|||||
|
|
// интерфейса |
с помощью Windows |
Forms, |
|||
using |
System.Data; |
// |
Общие типы |
для работы |
с данными, |
||
using |
System.Data.SqlClient; |
// |
Типы для |
доступа к данным MS |
SQL Server. |
||
После указания ряда необходимых пространств имен (и добавления ссылки на сборки, в которых они находятся), можно свободно создавать экземпляры типов, которые в них содержатся.
Например, при желании создать экземпляр класса Bitmap (определенного в пространстве имен System.Drawing), можно написать следующий код:
// Перечисляем используемые в данном файле // пространства имен явным образом. using System;
using System.Drawing; class Program
{
public void DisplayLogo ()
{
//Создаем растровое изображение
//размером 20*20 пикселей.
Bitmap companyLogo = new Bitmap(20, 20);
}
}
Благодаря импортированию в этом коде пространства имен System.Drawing, компилятор сможет определить, что класс Bitmap является членом данного пространства имен. Если пространство имен System.Drawing не указать, компилятор сообщит об ошибке.
Хотя определение типа с использованием полностью уточненного имени позволяет делать код более удобным для восприятия, трудно не согласиться с тем, что применение поддерживаемого в C# ключевого слова using, в свою очередь, позволяет значительно сократить количество печатаемых знаков.
Важно помнить о том, что ключевое слово using является просто сокращенным способом указания полностью уточненного имени. Поэтому любой из этих подходов приводит к получению одного и того же CIL-кода (с учетом того факта, что в CIL-коде всегда применяются полностью уточненные имена) и не сказывается ни на производительности, ни на размере сборки.
3.3.3.Типы данных, литералы, переменные, константы
Вцелом, типы данных, доступные в языке программирования, определяют те виды задач, для решения которых можно применять данный язык. Как и следовало ожидать, в C# предоставляется богатый набор встроенных типов данных, что делает этот язык пригодным для самого широкого применения. Любой из этих типов данных может служить для создания переменных и констант, которые в языке C# называются литералами.
Типы данных имеют особенное значение в С#, поскольку это строго типизированный язык. Это означает, что все операции подвергаются строгому контролю со стороны компилятора на соответствие типов, причем недопустимые операции не компилируются. Следовательно, строгий контроль типов позволяет исключить ошибки и повысить надежность программ. Для обеспечения контроля типов все переменные, выражения и значения должны принадлежать к определенному типу. Такого понятия, как "бестиповая" переменная, в данном языке программирования вообще не существует (возможно использование так называемой динамической переменной, контроль типа которой производится на этапе выполнения программы, а не компиляции). Более того, тип значения определяет те операции, которые разрешается выполнять над ним. Операция, разрешенная для одного типа данных, может оказаться недопустимой для другого.
ВC# имеются две общие категории встроенных типов данных: типы значений и ссылочные типы. Они отличаются по содержимому переменной. Если переменная относится к типу значения, то она содержит само значение, например 3,1416 или 212. А если переменная относится к ссылочному типу, то она содержит ссылку на значение. Наиболее распространенным примером использования ссылочного типа является класс, но о классах и ссылочных типах речь пойдет позже. Сначала рассмотрим типы значений.
3.3.3.1. Типы значений в C#
В основу языка C# положены 13 типов значений. Все они называются простыми типами, поскольку состоят из единственного значения. Иными словами, они не состоят из двух или более значений. Они составляют основу системы типов С#, предоставляя простейшие, низкоуровневые элементы данных, которыми можно оперировать в программе. Простые типы данных иногда еще называют примитивными.
В C# строго определены пределы и характер действия каждого типа значения. Исходя из требований к переносимости программ, C# не допускает в этом отношении никаких компромиссов. Например, тип int должен быть одинаковым во всех средах выполнения. Но в этом случае отпадает необходимость переписывать код для конкретной платформы. И хотя строгое определение размерности типов значений может стать причиной незначительного падения производительности в некоторых средах, эта мера необходима для достижения переносимости программ.
Целочисленные типы
В C# определены девять целочисленных типов: char, byte, sbyte, short, ushort, int, uint, long и ulong. Но тип char применяется, главным образом, для представления символов и поэтому рассматривается далее в этой главе. Остальные восемь целочисленных типов предназначены для числовых расчетов. Ниже представлены их диапазон представления чисел и разрядность в битах.
Таблица 1. Целочисленные типы данных
Тип |
Разрядность в битах |
|
Диапазон представления чисел |
byte |
8 |
0 |
~ 255 |
sbyte |
8 |
-128 ~ +127 |
|
short |
16 |
-32768 ~ +32767 ( 215 ~ 215 1) |
|
ushort |
16 |
0 |
~ 65535 (0 ~ 216 1) |
int |
32 |
-2147483648 ~ +2147483647 ( 231 ~ 231 1) |
|
uint |
32 |
0 |
~ +4294967296 (0 ~ 232 1) |
long |
64 |
263 ~ 263 1 |
|
ulong |
64 |
0 |
~ 264 1 |
Как следует из приведенной выше таблицы, в C# определены оба варианта различных целочисленных типов: со знаком и без знака. Целочисленные типы со знаком отличаются от аналогичных типов без знака способом интерпретации старшего разряда целого числа. Так, если в программе указано целочисленное значение со знаком, то компилятор C# сгенерирует код, в котором старший разряд целого числа используется в качестве флага знака. Число считается положительным, если флаг знака равен 0, и отрицательным, если он равен 1. Отрицательные числа практически всегда представляются методом дополнения до двух, в соответствии с которым все двоичные разряды отрицательного числа сначала инвертируются, а затем к этому числу добавляется 1.
Вероятно, самым распространенным в программировании целочисленным типом является тип int. Переменные типа int нередко используются для управления циклами, индексирования массивов и математических расчетов общего назначения. Когда же требуется целочисленное значение с большим диапазоном представления чисел, чем у типа int, то для этой цели имеется целый ряд других целочисленных типов. Так, если значение нужно сохранить без знака, то для него можно выбрать тип uint, для больших значений со знаком — тип long, а для больших значений без знака — тип ulong.
Самыми мелкими целочисленными типами являются byte и sbyte. Тип byte представляет целые значения без знака в пределах от 0 до 255. Переменные типа byte особенно удобны для обработки исходных двоичных данных, например байтового потока, поступающего от некоторого устройства. А для представления мелких целых значений со знаком служит тип sbyte.
Типы для представления чисел с плавающей точкой
Типы с плавающей точкой позволяют представлять числа с дробной частью. В C# имеются две разновидности типов данных с плавающей точкой: float и double. Они представляют числовые значения с одинарной и двойной точностью соответственно.
Таблица 2. Вещественные типы данных
Тип |
Разрядность в битах |
Диапазон представления чисел |
Суффикс |
float |
32 |
5Е-45 ~ 3,4Е+38 |
F или f |
double |
64 |
5Е-324 ~ 1,7Е+308 |
Нет, d или D |
