Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
Для распределения памяти, выделенной под стек, служит оператор stackalloc.Им можно пользоваться лишь при инициализации локальных переменных. Нижеприведена общая форма этого оператора:тип *р = stackalloc тип[размер]
где р обозначает указатель, получающий адрес области памяти, достаточной для хранения объектов, имеющих указанный тип, в количестве, которое обозначает размер.Если же в стеке недостаточно места для распределения памяти, то генерируется исключение System.StackOverflowException. И наконец, оператор stackalloc можно использовать только в небезопасном коде.
Как правило, память для объектов выделяется из кучи — динамически распределяемой свободной области памяти. А выделение памяти из стека является исключением.Ведь переменные, располагаемые в стеке, не удаляются средствами "сборки мусора",а существуют только в течение времени выполнения метода, в котором они объявляются. После возврата из метода выделенная память освобождается. Преимущество применения оператора stackalloc заключается, в частности, в том, что в этом случае ненужно беспокоиться об очистке памяти средствами "сборки мусора".
Ниже приведен пример применения оператора stackalloc.// Продемонстрировать применение оператора stackalloc.using System;class UseStackAlloc { unsafe static void Main() { int* ptrs = stackalloc int[3]; ptrs[0] = 1; ptrs[1] = 2; ptrs[2] = 3; for(int i=0; i < 3; i++) Console.WriteLine(ptrs[i]); }}
Вот к какому результату приводит выполнение кода из данного примера.123Создание буферов фиксированного размера
Ключевое слово fixed находит еще одно применение при создании одномерныхмассивов фиксированного размера. В документации на C# такие массивы называютсябуферами фиксированного размера. Такие буферы всегда являются членами структуры.Они предназначены для создания структуры, в которой содержатся элементы массива,образующие буфер. Когда элемент массива включается в состав структуры, в ней, какправило, хранится лишь ссылка на этот массив. Используя буфер фиксированного размера, в структуре можно разместить весь массив. В итоге получается структура, пригодная в тех случаях, когда важен ее размер, как, например, в многоязыковом программировании, при согласовании данных, созданных вне программы на С#, или же когдатребуется неуправляемая структура, содержащая массив. Но буферы фиксированногоразмера можно использовать только в небезопасном коде.
Для создания буфера фиксированного размера служит следующая общая форма:fixed тип имя_буфера[размер];
где тип обозначает тип данных массива; имя_буфера — конкретное имя буфера фиксированного размера; размер — количество элементов, образующих буфер. Буферыфиксированного размера могут указываться только в структуре.
Для того чтобы стала очевиднее польза от буферов фиксированного размера, рассмотрим ситуацию, в которой программе ведения счетов, написанной на C++, требуется передать информацию о банковском счете. Допустим также, что учетная записькаждого счета организована так, как показано ниже.NameСтрока длиной 80 байтов, состоящая из 8-разрядных символов в коде ASCIIBalanceЧисловое значение типа double длиной 8 байтовIDЧисловое значение типа long длиной 8 байтов
В программе на C++ каждая структура содержит массив Name, тогда как в программе на C# в такой структуре хранится лишь ссылка на массив. Поэтому для правильногопредставления данных из этой структуры в C# требуется буфер фиксированного размера, как показано ниже.// Использовать буфер фиксированного размера.unsafe struct FixedBankRecord { public fixed byte Name[80]; // создать буфер фиксированного размера public double Balance; public long ID;}
Когда буфер фиксированного размера используется вместо массива Name, каждыйэкземпляр структуры FixedBankRecord будет содержать все 80 байтов массива Name.Именно таким образом структура и организована в программе на C++. Следовательно,общий размер структуры FixedBankRecord окажется равным 96, т.е. сумме ее членов.Ниже приведена программа, демонстрирующая этот факт.// Продемонстрировать применение буфера фиксированного размера.using System;// Создать буфер фиксированного размера.unsafe struct FixedBankRecord { public fixed byte Name[80]; public double Balance; public long ID;}class FixedSizeBuffer { // Пометить метод Main() как небезопасный. unsafe static void Main() { Console.WriteLine("Размер структуры FixedBankRecord: " + sizeof(FixedBankRecord)); }}
Эта программа дает следующий результат.Размер структуры FixedBankRecord: 96
Размер структуры FixedBankRecord оказывается в точности равным сумме ее членов, но так бывает далеко не всегда со структурами, содержащими буферы фиксированного размера. Ради повышения эффективности кода общая длина структуры может быть увеличена для выравнивания по четной границе, например по границе слова.Поэтому общая длина структуры может оказаться на несколько байтов больше, чемсумма ее членов, даже если в ней содержатся буферы фиксированного размера. Какправило, аналогичное выравнивание длины структуры происходит и в C++. Следует,однако, иметь в виду возможные отличия в этом отношении.
И наконец, обратите внимание на то, как в данной программе создается буфер фиксированного размера вместо массива Name.public fixed byte Name[80]; // создать буфер фиксированного размера
Как видите, размер массива указывается после его имени. Такое обозначение обычнопринято в C++ и отличается в объявлениях массивов в С#. В данном операторе распределяется по 80 байтов памяти в пределах каждого объекта типа FixedBankRecord.Обнуляемые типы
Начиная с версии 2.0, в C# внедрено средство, обеспечивающее изящное решениетипичной и не очень приятной задачи распознавания и обработки полей, не содержащих значения, т.е. неинициализированных полей. Это средство называется обнуляемымтипом. Для того чтобы стала более понятной суть данной задачи, рассмотрим примерпростой базы данных заказчиков, в которой хранится запись с именем, адресом, идентификационным номером заказчика, номером счета-фактуры и текущим остатком насчету. В подобной ситуации может быть вполне создан элемент данных заказчика, в котором одно или несколько полей не инициализированы. Например, заказчик можетпросто запросить каталог продукции, и в этом случае номер счета-фактуры не потребуется, а значит, его поле окажется неиспользованным.
Раньше для обработки неиспользуемых полей приходилось применять заполняющие значения или дополнительные поля, которые просто указывали, используетсяполе или нет. Безусловно, заполняющие значения пригодны лишь в том случае, еслиони подставляются вместо значения, которое в противном случае окажется недействительным, но так бывает далеко не всегда. А дополнительные поля, указывающие, используется поле или нет, пригодны во всех случаях, но их ввод и обработка вручную доставляют немало хлопот. Оба эти затруднения позволяет преодолеть обнуляемый тип.Основы применения обнуляемых типов
Обнуляемый тип — это особый вариант типа значения, представленный структурой. Помимо значений, определяемых базовым типом, обнуляемый тип позволяетхранить пустые значения (null). Следовательно, обнуляемый тип имеет такой же диапазон представления чисел и характеристики, как и его базовый тип. Он предоставляетдополнительную возможность обозначить значение, указывающее на то, что переменная данного типа не инициализирована. Обнуляемые типы являются объектами типаSystem.Nullаble<Т>, где Т — тип значения, которое не должно быть обнуляемым.
ПРИМЕЧАНИЕОбнуляемые эквиваленты могут быть только у типов значений.
Обнуляемый тип может быть указан двумя способами. Во-первых, объекты типаNullable<T>, определенного в пространстве имен System, могут быть объявлены явным образом. Так, в приведенном ниже примере создаются обнуляемые переменныетипа int и bool.System.Nullable<int> count;System.Nullable<bool> done;
И во-вторых, обнуляемый тип объявляется более кратким и поэтому чаще используемым способом с указанием знака ? после имени базового типа. В приведенномниже примере демонстрируется более распространенный способ объявления обнуляемых переменных типа int и bool.int? count;bool? done;