Информационные технологии
Операционные системы
Информационные технологии
Информационные технологии представляют собой широкий класс дисциплин и сфер деятельности, которые относятся к технологиям создания, хранения, управления, ... читать далее »
Статьи по ИТ
28.06.2009 15:09

Типы данных C#. Информационные технологии.

С# является жестко типизированным языком. При его использовании вы должны объявлять тип каждого объекта, который создаете (например, целые числа, числа с плавающей точкой, строки, окна, кнопки, и т. д.), и компилятор поможет вам избежать ошибок, связанных с присвоением переменным значений только того типа, который им соответствует. Тип объекта указывает компилятору размер объекта (например, объект типа int занимает в памяти 4 байта) и его свойства (например, форма может быть видима и невидима, и т.д.).

Подобно языкам C++ и Java, C# подразделяет типы на два вида: встроенные типы, которые определены в языке, и определяемые пользователем типы, которые выбирает программист.
С# также подразделяет типы на две другие категории: размерные и ссылочные. Основное различие между ними — это способ, которым их значения сохраняются в памяти. Размерные типы сохраняют свое фактическое значение в стеке. Ссылочные типы хранят в стеке лишь адрес объекта, а сам объект сохраняется в куче. Куча — основная память программ, доступ к которой осуществляется на много медленнее чем к стеку. Если вы работаете с очень большими объектами, то сохранение их в куче имеет много преимуществ.
В следующих уроках будут подробно рассмотрены различные преимущества и недостатки работы с ссылочными типами.
С# также поддерживает и указатели на типы, но они редко употребляются. Применение указателей связано с использованием неуправляемого кода.

ОСОБЕННОСТИ ИСПОЛЬЗОВАНИЯ СТЕКА И КУЧИ

Стек — это структура данных, которая сохраняет элементы по принципу: первым пришел, последним ушел (полная противоположность очереди). Стек относится к области памяти, поддерживаемой процессором, в которой сохраняются локальные переменные. Доступ к стеку во много раз быстрее, чем к общей области памяти, поэтому использование стека для хранения данных ускоряет работу вашей программы. В С# размерные типы (например, целые числа) располагаются в стеке: для их значений зарезервирована область в стеке, и доступ к ней осуществляется по названию переменной.
Ссылочные типы (например, объекты) располагаются в куче. Куча —это оперативная память вашего компьютера. Доступ к ней осуществляется медленнее, чем к стеку. Когда объект располагается в куче, то переменная хранит лишь адрес объекта. Этот адрес хранится в стеке. По адресу программа имеет доступ к самому объекту, все данные которого сохраняются в общем куске памяти (куче).
«Сборщик мусора» уничтожает объекты, располагающиеся в стеке, каждый раз, когда соответствующая переменная выходит за область видимости. Таким образом, если вы объявляете локальную переменную в пределах функции, то объект будет помечен как объект для «сборки мусора». И он будет удален из памяти после завершения работы функции.
Объекты в куче тоже очищаются сборщиком мусора, после того как конечная ссылка на них будет разрушена.

ВСТРОЕННЫЕ ТИПЫ

Язык С# предоставляет программисту широкий спектр встроенных типов, которые соответствуют CLS (Common Language Specification) и отображаются на основные типы платформы .NET. Это гарантирует, что объекты, созданные на С#, могут успешно использоваться наряду с объектами, созданными на любом другом языке программирования, поддерживающем .NET CLS (например, VB.NET).
Каждый тип имеет строго заданный для него размер, который не может изменяться. В отличие от языка C++, в С# тип int всегда занимает 4 байта, потому что отображается к Int32 в .NET CLS. Представленная ниже таблица содержит список всех встроенных типов, предлагаемых С#.

Тип

Область значений

Размер

sbyte

-128 до 127

Знаковое 8-бит целое

byte

0 до 255

Беззнаковое 8-бит целое

char

U+0000 до U+ffff

16-битовый символ Unicode

bool

true или false

1 байт

short

-32768 до 32767

Знаковое 16-бит целое

ushort

0 до 65535

Беззнаковое 16-бит целое

int

-2147483648 до 2147483647

Знаковое 32-бит целое

uint

0 до 4294967295

Беззнаковое 32-бит целое

long

-9223372036854775808 до 9223372036854775807

Знаковое 32-бит целое

ulong

0 до 18446744073709551615

Беззнаковое 32-бит целое

float

±1,5*10-45 до ±3,4*1033

4 байта, точность — 7 разрядов

double

±5*10-324 до ±1,7*10306

8 байт, точность —16 разрядов

decimal

12 байт, точность — 28 разрядов

В дополнение к этим примитивным типам С# может иметь объекты типа enum и struct.

Преобразование встроенных типов

Объекты одного типа могут быть преобразованы в объекты другого типа неявно или явно. Неявные преобразования происходят автоматически, компилятор делает это вместо вас. Явные преобразования осуществляются, когда вы «приводите» значение к другому типу. Неявные преобразования гарантируют также, что данные не будут потеряны. Например, вы можете неявно приводить от short (2 байта) к int (4 байта).
Независимо от того, какой значение находится в short, оно не потеряется при преобразовании к int:


short x = 1;
int у = х; //неявное преобразование

Если вы делаете обратное преобразование, то, конечно же, можете потерять информацию. Если значение в int больше, чем 32.767, оно будет усечено при преобразовании. Компилятор не станет выполнять неявное преобразование от int к short:


short х;
int у - 5;
х = у; //не скомпилируете

Вы должны выполнить явное преобразование, используя оператор приведения:


short х;
int у - 5;
х = (short) у;//OK

Переменные

Переменная — это расположение в памяти объекта определенного типа. В приведенных выше примерах х и у — переменные. Переменные могут иметь значения, которыми они проинициализированы, или эти значения могут быть изменены программно.
Назначение значений переменным Чтобы создать переменную, вы должны задать тип переменной и затем дать этому типу имя. Вы можете проинициализировать переменную во время ее объявления или присвоить ей новое значение во время выполнения программы. Вот пример программы, который в первом случае использует инициализацию для присвоения значения переменной, во втором случае использует присвоение значения переменной с помощью оператора «=»:


class Variables
    {
        static void Main()
        {
            int myInt = 10;
            System.Console.WriteLine("Инициализированная переменная myInt: {0} ", myInt);
            myInt = 5;
            System.Console.WriteLine("Переменная myInt после присвоения значения: {0}", myInt);
            Console.ReadLine();
        }

    }

Результат работы этой программы будет следующий:
Инициализированная переменная mylnt: 10
myInt после присвоения значения: 5
Здесь строка:
int myInt = 10;
означает объявление и инициализацию переменной mylnt. Строка:
myInt= 5 ;
означает присвоение переменной mylnt значения 5.

Определение значений переменных

С# требует определения значений, то есть переменные перед использованием должны быть инициализированы. Чтобы проверить это правило, рассмотрим следующий пример:


class Variables
    {
        static void Main()
        {
            int myInt;
            System.Console.WriteLine("Неинициализированная переменная myInt: {0}", myInt);
            myInt = 5;
            System.Console.WriteLine("После присвоения переменной myInt: {0}", myInt);

        }
    }

Если вы попробуете скомпилировать этот пример, компилятор отобразит следующее сообщение об ошибке:
error CS3165: Use of unassigned local variable 'mylnt'
Нельзя использовать неинициализированную переменную в С#. У вас может сложиться впечатление, что вы должны инициализировать каждую переменную в программе. Это не так. Вы должны лишь назначить переменной значение прежде, чем попытаетесь ее использовать. Ниже представлен пример правильной программы без использования инициализации переменной


class Variables
    {
        static void Main()
        {
            int myInt;
            myInt = 10;
            System.Console.WriteLine("Инициализированная переменная myInt: {0}", myInt);
            myInt = 5;
            System.Console.WriteLine("После присвоения значения, myInt: {0}", myInt);

        }
    }

В данном примере вместо инициализации выбирается присвоение значения переменной mylnt до ее использования:
mylnt = 10;

Константы

Константа — это переменная, значение которой не может быть изменено. Переменные — это более гибкий способ хранения данных. Однако иногда вы хотите гарантировать сохранение значения определенной переменной.
Например, число pi. Как известно, значение этого числа никогда не изменяется. Следовательно, вы должны гарантировать, что переменная, хранящая это число, не изменит своего значения на протяжении всей работы программы. Ваша программа будет лучше читаемой, если вы вместо записи:
у = х*3.1415926535897932384626433832795
будете использовать переменную, которая хранит значение pi. В таком случае используемой переменной должна быть константа:
const double pi = 3.1415926535897932384626433832795;
у = х * pi;
Существует три разновидности константы: литералы, символические константы и перечисления. Рассмотрим следующий случай:
х = 100;
Значение 100 — это литеральная константа. Значение 100 —это всегда 100. Вы не можете установить новое значение на 100. Вы не можете сделать так, чтобы 100 представляло значение 99. Символические константы устанавливают имя для некоторого постоянного значения. Вы объявляете символическую константу, используя ключевое слово const, и применяете следующий синтаксис для создания константы:
const тип идентификатор = значение;
Константа обязательно должна быть проинициализирована, и ее значение не может быть изменено во время выполнения программы. Например:
const double pi = 3.1415926535897932384626433832795;
pi = 5.0; //недопустимая операция
В этом примере число 3.14...—литеральная константа, a pi — символическая константа типа double. Ниже представлен пример использования символических констант.


class Constants
    {
        static void Main () 
        {
            const double pi = 3.1415926535897932384626433832795;
            const double g = 9.81; //гравитационная постоянная
            System.Console.WriteLine("Число пи: {0}" ,pi);
            System.Console.WriteLine("Гравитационная постоянная: {0}",g);
        }
    }

Результат работы программы будет следующий:
Число пи: 3.1415926535897932384626433832795
Гравитационная постоянная: 9.81
В последнем примере создаются две символические целочисленные константы: pi и д. Эти константы преследуют ту же цель, что и использование их литеральных значений. Но данные константы имеют имена, которые несут в себе гораздо больше информации об используемом значении, чем просто набор цифр.
Для того чтобы убедиться, что константные значения не могут быть изменены во время выполнения программы, добавьте в код следующую строку:
g = 10;
Во время компиляции вы получите следующее сообщение об ошибке:
error CS0131: The left-hand side of an assignment must be a variable, property or indexer.

Перечисления

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

const int maryBirthday = 1955;
const int ivanBirthday = 1980;
const int pavelBirthday = 1976;

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

enum FriendsBirthday
{
const int maryBirthday = 1955;
const int ivanBirthday = 1980;
const int pavelBirthday = 1976;
}

Теперь три символические константы являются элементами одного перечисления типа FriendsBirthday.
Каждое перечисление имеет свой базовый тип, которым может быть любой встроенный целочисленный тип С# (int, long, short и т. д.), за исключением char.
Перечисление задается следующим образом:
[атрибуты] [модификаторы] enum идентификатор[: базовый тип] (список перечислений);
Атрибуты и модификаторы будут рассматриваться далее. Пока давайте остановимся на второй части этого объявления. Перечисление начинается с ключевого слова enum, которое сопровождается идентификатором типа:
enum MyEnurnerators
Базовый тип — основной тип для перечисления. Если вы не учитываете этот описатель при создании перечисления, то будут использоваться значения по умолчанию int. Но вы можете применить любой из целочисленных типов (например, ushort, long), за исключением char. Например, следующий фрагмент кода объявляет перечисление целых чисел без знака (uint):

enum Sizes: uint
{
Small = 1,
Middle = 2,
Large = 3
}

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

enum Sizes: uint
{
Small,
Middle,
Large
}

Если вы объявите свое перечисление следующим образом:

enum Sizes: uint
{
Small,
Middle = 20,
Large
}

то элементы перечисления будут иметь такие числовые значения:
Small = 0;
Middle = 20;
Large = 21.
Давайте рассмотрим пример, который наглядно показывает, каким образом перечисления помогают упростить код приложения.

class ScreenResolutions
{
//перечисление размеров мониторов в дюймах
    enum Screens
        {
            Small = 14,
            Medium = 17,
            Large = 19,
            SuperLarge = 21
        }
    static void Main()
        {
            System.Console.WriteLine("Самые маленькие мониторы имеют размер: {0}", (int) Screens.Small );
            System.Console.WriteLine("Самые большие мониторы имеют размер: {0}", (int) Screens.SuperLarge);
        }
   }

Как видите, значение перечисления (SuperLarge) должно быть специфицировано именем перечисления (Screens). По умолчанию, значение перечисления представляется его символическим именем (типа Small или Medium). Если вы хотите отобразить значение константы перечисления, то должны привести константу к ее основному типу (в данном случае int).
Целочисленное значение передается в функцию WriteLine, и оно отображается на экране.

Строковые константы

Для объявления в программе константной строки вам необходимо заключить содержимое строки в двойные кавычки ("My string"). Вы можете делать это практически в любом месте программы: в передаче параметров функции, в инициализации переменных. Мы уже неоднократно применяли строковые константы при выводе данных на экран.
System.Console.WriteLine("Самые большие мониторы имеют размер: {С}", (int) Screens.SuperLarge);
Здесь в качестве одного из параметров функции используется строка "Самые большие мониторы имеют размер: {0}".
string strMessage = "Здравствуй Мир!";
В данном случае константная строка «Здравствуй Мир!» инициализирует переменную strMessage.

Массивы

Более детально работа с массивами будет рассмотрена в последующих уроках. Пока расскажем о массивах с точки зрения типов данных.
Массивы в С# несколько отличаются от других С-подобных языков.
Начнем сразу с примеров. Пример первый:

int [ ] к; //к - массив
k=new int [3]; //Определяем массив из 3 целых
к[0]=-5; к[1]-4; к[2]=55; //Задаем элементы массива
//Выводим третий элемент массива
Console.WriteLine(к[2] .ToString ());

Смысл приведенного фрагмента ясен из комментариев. Обратите внимание на некоторые особенности. Во-первых, массив определяется именно как
int[] к;
а не как один из следующих вариантов:

int k[]; //Неверно!
int k [3]; //Неверно !
int [3] к; //Неверно!

Во-вторых, так как массив представляет собой ссылочный объект, то для создания массива необходима строка
k=new int [3];
Именно в ней мы и определяем размер массива. Кроме того, возможны конструкции вида
int [] k = new int [3];
Элементы массива можно задавать сразу при объявлении. Например:
int [] к = {-5, 4, 55} ;
Разумеется, приведенные конструкции применимы не только к типу int и не только к массиву размера 3.
В С#, как и в C/C++, нумерация элементов массива идет с нуля. Таким образом, в нашем примере начальный элемент массива — это k[0], a последний — к[2]. Элемента к[3], естественно, нет.
Теперь переходим к многомерным массивам. Вот так задается двумерный массив:

int [] k = new int [2, 3];

Обратите внимание, что пара квадратных скобок только одна. В нашем примере у массива 6 (=2*3) элементов (к[0,0] — первый, к[1,2] — последний).
Аналогично мы можем задавать многомерные массивы. Вот пример трехмерного массива:

int [] к = new int [10,10,10];

А вот так можно сразу инициализировать многомерные массивы:

int[,] k = {{ 2, -2 }, { 3, -22 }, { 0, 4 }};

Приведенные выше примеры многомерных массивов называются прямоугольными. Если их представить в виде таблицы (в двумерном случае), то массив будет представлять собой прямоугольник.
Наряду с прямоугольными массивами существуют так называемые ступенчатые. Вот пример:
//Объявляем 2-мерный ступенчатый массив
int[][] k = new int [2][];
//Объявляем 0-й элемент нашего ступенчатого массива
//Это опять массив и в нем 3 элемента
k[0]=new int[3];
//Объявляем 1-й элемент нашего ступенчатого массива
//Это опять массив и в нем 8 элементов
k[1]=new int[8];
k[1][7]=100; //записываем 100 в последний элемент массива

Обратите внимание, что у ступенчатых массивов мы задаем несколько пар квадратных скобок (по размерности массива). И точно так же мы что-нибудь делаем с элементами массива — записываем, читаем и т. п.
Самая важная и интересная возможность ступенчатых массивов — это их «непрямоугольность». Так, в приведенном выше примере в первой «строке» массива к три целых числа, а во второй — восемь. Часто это оказывается очень кстати.

Источник

© WIKI.RU, 2008–2017 г. Все права защищены.