Стек—это структура данных, которая сохраняет элементы по принципу: первым пришел, последним ушел. Стек относится к области памяти, поддерживаемой процессором, в которой сохраняются локальные переменные. Доступ к стеку во много раз быстрее, чем к общей области памяти, поэтому использование стека для хранения данных ускоряет работу вашей программы. В С# размерные типы располагаются в стеке: для их значений зарезервирована область в стеке, и доступ к ней осуществляется по названию переменной.
Ссылочные типы (например, объекты) располагаются в куче. Куча — это оперативная память вашего компьютера. Доступ к ней осуществляется медленнее, чем к стеку. Когда объект располагается в куче, то переменная хранит лишь адрес объекта. Этот адрес хранится в стеке. По адресу программа имеет доступ к самому объекту, все данные которого сохраняются в общем куске памяти (куче).
«Сборщик мусора» уничтожает объекты, располагающиеся в стеке, каждый раз, когда соответствующая переменная выходит за область видимости. Таким образом, если вы объявляете локальную переменную в пределах функции, то объект будет помечен как объект для «сборки мусора». И он будет удален из памяти после завершения работы функции.
Объекты в куче тоже очищаются сборщиком мусора, после того как конечная ссылка на них будет разрушена.
Для каждого типа данных C# существует соответствующий тип данных в CRL (Common Language Runtime). Это, в частности, означает, что каждый тип имеет два названия - полный (из CLR, его можно использовать в любом языке .NET) и сокращенный, который используется в C#. Что за название использовать - это дело вкуса. Приведем как полные названия (из CLR), так и краткие.
Логический тип
Имя типа
Системный тип
Значения
Размер
bool
System.Boolean
true, false
8 бит
Арифметические целочисленные типы
Имя типа
Системный тип
Диапазон
Размер
sbyte
System.SByte
-128 — 128
Знаковое, 8 Бит
byte
System.Byte
0 — 255
Беззнаковое, 8 Бит
short
System.Short
-32768 —32767
Знаковое, 16 Бит
ushort
System.UShort
0 — 65535
Беззнаковое, 16 Бит
int
System.Int32
≈(-2*109 —2*109)
Знаковое, 32 Бит
uint
System.UInt32
≈(0 — 4*109)
Беззнаковое, 32 Бит
long
System.Int64
≈(-9*1018 — 9*1018)
Знаковое, 64 Бит
ulong
System.UInt64
≈(0— 18*1018)
Беззнаковое, 64 Бит
Арифметический тип с плавающей точкой
Имя типа
Системный тип
Диапазон
Точность
float
System.Single
+1.5*10-45 - +3.4*1038
7 цифр
double
System.Double
+5.0*10-324 - +1.7*10308
15-16 цифр
Арифметический тип с фиксированной точкой
Имя типа
Системный тип
Диапазон
Точность
decimal
System.Decimal
+1.0*10-28 - +7.9*1028
28-29 значащих цифр
Символьные типы
Имя типа
Системный тип
Диапазон
Точность
char
System.Char
U+0000 - U+ffff
16 бит Unicode символ
string
System.String
Строка из символов Unicode
Объектный тип
Имя типа
Системный тип
Примечание
object
System.Object
Прародитель всех встроенных и пользовательских типов
Язык C# в большей степени, чем язык C++, является языком объектного программирования. В языке C# сглажено различие между типом и классом. Все типы - встроенные и пользовательские - одновременно являются классами, связанными отношением наследования. Родительским, базовым классом является класс Object. Все остальные типы или, точнее, классы являются его потомками, наследуя методы этого класса. У класса Object есть четыре наследуемых метода:
- bool Equals (object obj) - проверяет эквивалентность текущего объекта и объекта, переданного в качестве аргумента;
- System.Type GetType () - возвращает системный тип текущего объекта;
- string ToString () - возвращает строку, связанную с объектом. Для арифметических типов возвращается значение, преобразованное в строку.
Естественно, что все встроенные типы нужным образом переопределяют методы родителя и добавляют собственные методы и свойства. Учитывая, что и типы, создаваемые пользователем, также являются потомками класса Object, то для них необходимо переопределить методы родителя, если предполагается использование этих методов; реализация родителя, предоставляемая по умолчанию, не обеспечивает нужного эффекта.
int x=11;
int v = new Int32();
v = 007;
string s1 = "Agent";
s1 = s1 + v.ToString() +x.ToString();
В этом примере переменная x объявляется как обычная переменная типа int. В то же время для объявления переменной v того же типа int используется стиль, принятый для объектов. В объявлении применяется конструкция new и вызов конструктора класса. В операторе присваивания, записанном в последней строке фрагмента, для обеих переменных вызывается метод ToString, как это делается при работе с объектами. Этот метод, наследуемый от родительского класса Object, переопределенный в классе int, возвращает строку с записью целого. Сообщу еще, что класс int не только наследует методы родителя - класса Object, - но и дополнительно определяет метод CompareTo, выполняющий сравнение целых, и метод GetTypeCode, возвращающий системный код типа.
Так что же такое после этого int: тип или класс? int - это и тип, и класс. В зависимости от контекста x может восприниматься как переменная типа int или как объект класса int. Это же верно и для всех остальных value- типов. Замечу еще, что все встроенные типы фактически реализованы как структуры, представляющие частный случай класса.
Рассмотрим пример. Класс Testing представляет собой набор данных разного типа, над которыми выполняются операции, иллюстрирующие преобразования типов. Вот описание класса Testing:
using System;
namespace Types {
public class Testing {
byte b = 255;
int x = 11;
uint ux = 1111;
float y = 5.5f;
double dy = 5.55;
string s = "Hello!";
string s1 = "25";
object obj = new Object();
// Далее идут методы класса, приводимые по ходу
// описания примеров
В набор данных класса входят скалярные данные арифметического типа, относящиеся к значимым типам, переменные строкового типа и типа object, принадлежащие ссылочным типам. Рассмотрим закрытый (private) метод этого класса - процедуру WhoIsWho с формальным аргументом класса Object. Процедура выводит на консоль переданное ей имя аргумента, его тип и значение. Вот ее текст:
void WhoIsWho(string name, object any) {
Console.WriteLine("type {0} is {1} , value is {2}",
name, any.GetType(), any.ToString());
}
Вот открытый (public) метод класса Testing, в котором многократно вызывается метод WhoIsWho с аргументами разного типа:
public void WhoTest() {
WhoIsWho("x",x); //Type x is System.Int32, value is 11
WhoIsWho("ux",ux); //Type ux is System.UInt32, value is 1111
WhoIsWho("y",y);
WhoIsWho("dy",dy);
WhoIsWho("s",s);
WhoIsWho("11 + 5.55 + 5.5f",11 + 5.55 + 5.5f);
//Type 11 + 5.55 + 5.5f is System.Double, value is 22.05
obj = 11 + 5.55 + 5.5f;
WhoIsWho("obj",obj); //Type obj is System.Double, value is 22.05
} }}
Заметьте, сущность any - формальный аргумент класса Object при каждом вызове - динамически изменяет тип, связываясь с объектом, заданным фактическим аргументом. Поэтому тип аргумента, выдаваемый на консоль, - это тип фактического аргумента. Заметьте также, что наследуемый от класса Object метод GetType возвращает тип FCL, то есть тот тип, на который отражается тип языка и с которым реально идет работа при выполнении модуля. В большинстве вызовов фактическим аргументом является переменная - соответствующее свойство класса Testing, но в одном случае передается обычное арифметическое выражение, автоматически преобразуемое в объект. Аналогичная ситуация имеет место и при выполнении присваивания в рассматриваемой процедуре.