Прежде чем объяснять различные типы данных, доступные в C #, важно упомянуть, что C # - это строго типизированный язык. Это означает, что каждая переменная, константа, входной параметр, тип возвращаемого значения и вообще каждое выражение, оценивающее значение, имеет тип.
Каждый тип содержит информацию, которая будет встроена компилятором в исполняемый файл в виде метаданных, которые будут использоваться средой CLR для обеспечения безопасности типов при выделении и освобождении памяти.
Если вы хотите узнать, сколько памяти выделяет конкретный тип, вы можете использовать оператор sizeof следующим образом:
static void Main()
{
var size = sizeof(int);
Console.WriteLine($"int size:{size}");
size = sizeof(bool);
Console.WriteLine($"bool size:{size}");
size = sizeof(double);
Console.WriteLine($"double size:{size}");
size = sizeof(char);
Console.WriteLine($"char size:{size}");
}
Вывод покажет количество байтов, выделенных каждой переменной.
int size:4
bool size:1
double size:8
char size:2
Информация, относящаяся к каждому типу:
- Требуемое место для хранения.
- Максимальные и минимальные значения. Например, тип Int32 принимает значения от 2147483648 до 2147483647.
- Базовый тип, от которого он наследуется.
- Место, где будет выделяться память для переменных во время выполнения.
- Виды разрешенных операций.
Члены (методы, поля, события и т. Д.), Содержащиеся в типе. Например, если мы проверим определение типа int, мы найдем следующую структуру и члены:
namespace System
{
[ComVisible(true)]
public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<Int32>, IEquatable<Int32>
{
public const Int32 MaxValue = 2147483647;
public const Int32 MinValue = -2147483648;
public static Int32 Parse(string s, NumberStyles style, IFormatProvider provider);
...
}
}
Управление памятью
Когда в операционной системе запущено несколько процессов и объема оперативной памяти недостаточно, чтобы вместить все это, операционная система сопоставляет части жесткого диска с оперативной памятью и начинает хранить данные на жестком диске. Операционная система будет использовать специальные таблицы, в которых виртуальные адреса сопоставлены с соответствующими физическими адресами для выполнения запроса. Эта возможность управления памятью называется виртуальной памятью.
В каждом процессе доступная виртуальная память организована в следующие 6 разделов, но для актуальности этой темы мы сосредоточимся только на стеке и куче.
Стек
Стек представляет собой структуру данных LIFO (последний пришел - первым ушел), размер которой зависит от операционной системы (по умолчанию для компьютеров ARM, x86 и x64 резерв Windows составляет 1 МБ, а в Linux - от 2 МБ до 8 МБ в зависимости от версия).
Этот раздел памяти автоматически управляется ЦП. Каждый раз, когда функция объявляет новую переменную, компилятор выделяет новый блок памяти размером с его размер в стеке, а когда функция завершается, блок памяти для переменной освобождается.
Куча.
Эта область памяти не управляется ЦП автоматически, и ее размер больше, чем размер стека. Когда вызывается ключевое слово new, компилятор начинает поиск первого свободного блока памяти, который соответствует размеру запроса. и когда он его находит, он помечается как зарезервированный с помощью встроенной функции C malloc () и возвращает указатель на это место. Также возможно освободить блок памяти с помощью встроенной функции C free (). Этот механизм вызывает фрагментацию памяти и должен использовать указатели для доступа к правому блоку памяти, он медленнее, чем стек для выполнения операций чтения / записи.
Пользовательские и встроенные типы.
Хотя C # предоставляет стандартный набор встроенных типов, представляющих целые числа, логические значения, текстовые символы и т. Д., Вы можете использовать такие конструкции, как struct, class, interface и enum, для создания ваших собственных типов.
Пример настраиваемого типа с использованием конструкции struct:
struct Point
{
public int X;
public int Y;
};
Типы значений и ссылочные типы
Мы можем разделить тип C # на следующие категории:
- Типы значений
- Типы ссылок
Типы значений Типы
значений являются производными от класса System.ValueType, а переменные этого типа содержат свои значения в выделенной им памяти в стеке. Две категории типов значений - это структура и перечисление.
В следующем примере показан член типа boolean. Как видите, явной ссылки на класс System.ValueType нет, это происходит потому, что этот класс наследуется структурой.
namespace System
{
[ComVisible(true)]
public struct Boolean : IComparable, IConvertible, IComparable<Boolean>, IEquatable<Boolean>
{
public static readonly string TrueString;
public static readonly string FalseString;
public static Boolean Parse(string value);
...
}
}
Ссылочные типы
С другой стороны, ссылочные типы не содержат фактических данных, хранящихся в переменной, а содержат адрес кучи, в которой хранится значение. Категории ссылочных типов - это классы, делегаты, массивы и интерфейсы.
Во время выполнения, когда объявляется переменная ссылочного типа, она содержит значение null до тех пор, пока ей не будет присвоен объект, созданный с использованием ключевых слов new.
В следующем примере показаны члены универсального типа List.
namespace System.Collections.Generic
{
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(Generic.Mscorlib_CollectionDebugView<>))]
[DefaultMember("Item")]
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>
{
...
public T this[int index] { get; set; }
public int Count { get; }
public int Capacity { get; set; }
public void Add(T item);
public void AddRange(IEnumerable<T> collection);
...
}
}
Если вы хотите узнать адрес памяти определенного объекта, класс System.Runtime.InteropServices предоставляет способ доступа к управляемым объектам из неуправляемой памяти. В следующем примере мы собираемся использовать статический метод GCHandle.Alloc () для выделения дескриптора строки, а затем метод AddrOfPinnedObject для получения ее адреса.
string s1 = "Hello World";
GCHandle gch = GCHandle.Alloc(s1, GCHandleType.Pinned);
IntPtr pObj = gch.AddrOfPinnedObject();
Console.WriteLine($"Memory address:{pObj.ToString()}");
Выход будет
Memory address:39723832
Ссылки
Официальная документация: https://docs.microsoft.com/en-us/cpp/build/reference/stack-stack-allocations?view=vs-2019